I am working on making a web server with java.
The task is...
The Host header of HTTP/1.1 must be resolvable. For example, the web server implementing the request of a.com and b.com should be able to provide different data depending on the domain.
The following should be manageable in a configuration file. (ex properties, json, xml, yml, etc.)
2-1. The operating port of the implemented web server must be configurable. (ex 80, 8080)
2-2. HTTP_DOC_ROOT must be designated for each HTTP/1.1 Host.
2-3. It should be possible to designate the path of the HTML file on the server to output in case of 403, 404, or 500 errors for each HTTP/1.1 host.
Below is sample code.
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
public class HttpServer {
private static final Logger logger = Logger.getLogger(HttpServer.class.getCanonicalName());
private static final int NUM_THREADS = 50;
private static final String INDEX_FILE = "index.html";
private final File rootDirectory;
private final int port;
public HttpServer(File rootDirectory, int port) throws IOException {
if (!rootDirectory.isDirectory()) {
throw new IOException(rootDirectory
+ " does not exist as a directory");
}
this.rootDirectory = rootDirectory;
this.port = port;
}
public void start() throws IOException {
ExecutorService pool = Executors.newFixedThreadPool(NUM_THREADS);
try (ServerSocket server = new ServerSocket(port)) {
logger.info("Accepting connections on port : " + server.getLocalPort());
logger.info("Document Root: " + rootDirectory);
while (true) {
try {
logger.info("Runnable Start ");
Socket request = server.accept();
Runnable r = new RequestProcessor(rootDirectory, INDEX_FILE, request);
logger.info("Runnable r1 : " + r);
pool.submit(r);
logger.info("Runnable r2 : " + r);
} catch (IOException ex) {
logger.log(Level.WARNING, "Error accepting connection", ex);
}
}
}
}
public static void main(String[] args) {
// get the Document root
File docroot;
try {
docroot = new File(args[0]);
} catch (ArrayIndexOutOfBoundsException ex) {
System.out.println("Usage: java JHTTP docroot port : " + ex);
return;
}
// set the port to listen on
int port;
try {
port = Integer.parseInt(args[1]);
if (port < 0 || port > 65535) port = 80;
} catch (RuntimeException ex) {
port = 80;
}
try {
HttpServer webserver = new HttpServer(docroot, port);
webserver.start();
} catch (IOException ex) {
logger.log(Level.SEVERE, "Server could not start", ex);
}
}
}
import java.io.*;
import java.net.Socket;
import java.net.URLConnection;
import java.nio.file.Files;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
public class RequestProcessor implements Runnable {
private final static Logger logger = Logger.getLogger(RequestProcessor.class.getCanonicalName());
private File rootDirectory;
private String indexFileName = "index.html";
private Socket connection;
public RequestProcessor(File rootDirectory, String indexFileName, Socket connection) {
if (rootDirectory.isFile()) {
throw new IllegalArgumentException(
"rootDirectory must be a directory, not a file");
}
try {
rootDirectory = rootDirectory.getCanonicalFile();
} catch (IOException ex) {
}
this.rootDirectory = rootDirectory;
if (indexFileName != null)
this.indexFileName = indexFileName;
this.connection = connection;
}
@Override
public void run() {
// for security checks
String root = rootDirectory.getPath();
logger.info("RequestProcessor Start!!!!!!!");
logger.info( "root : " + root);
try {
OutputStream raw = new BufferedOutputStream(connection.getOutputStream());
Writer out = new OutputStreamWriter(raw);
Reader in = new InputStreamReader(new BufferedInputStream(connection.getInputStream()), "UTF-8");
StringBuilder requestLine = new StringBuilder();
while (true) {
int c = in.read();
if (c == '\r' || c == '\n')
break;
requestLine.append((char) c);
}
logger.info( "requestLine : " + requestLine);
String get = requestLine.toString();
logger.info("connection.getRemoteSocketAddress" + connection.getRemoteSocketAddress() + " " + get);
String[] tokens = get.split("\\s+");
String method = tokens[0];
String version = "";
if (method.equals("GET")) {
String fileName = tokens[1];
if (fileName.endsWith("/")) fileName += indexFileName;
String contentType =
URLConnection.getFileNameMap().getContentTypeFor(fileName);
if (tokens.length > 2) {
version = tokens[2];
}
File theFile = new File(rootDirectory, fileName.substring(1, fileName.length()));
if (theFile.canRead()
// Don't let clients outside the document root
&& theFile.getCanonicalPath().startsWith(root)) {
byte[] theData = Files.readAllBytes(theFile.toPath());
if (version.startsWith("HTTP/")) { // send a MIME header
sendHeader(out, "HTTP/1.0 200 OK", contentType, theData.length);
}
// send the file; it may be an image or other binary data
// so use the underlying output stream
// instead of the writer
raw.write(theData);
raw.flush();
} else {
// can't find the file
String body = new StringBuilder("<HTML>\r\n")
.append("<HEAD><TITLE>File Not Found</TITLE>\r\n")
.append("</HEAD>\r\n")
.append("<BODY>")
.append("<H1>HTTP Error 404: File Not Found</H1>\r\n")
.append("</BODY></HTML>\r\n")
.toString();
if (version.startsWith("HTTP/")) { // send a MIME header
sendHeader(out, "HTTP/1.0 404 File Not Found", "text/html; charset=utf-8", body.length());
}
out.write(body);
out.flush();
}
} else {
// method does not equal "GET"
String body = new StringBuilder("<HTML>\r\n").append("<HEAD><TITLE>Not Implemented</TITLE>\r\n").append("</HEAD>\r\n")
.append("<BODY>")
.append("<H1>HTTP Error 501: Not Implemented</H1>\r\n")
.append("</BODY></HTML>\r\n").toString();
if (version.startsWith("HTTP/")) { // send a MIME header
sendHeader(out, "HTTP/1.0 501 Not Implemented",
"text/html; charset=utf-8", body.length());
}
out.write(body);
out.flush();
}
} catch (IOException ex) {
logger.log(Level.WARNING, "Error talking to " + connection.getRemoteSocketAddress(), ex);
} finally {
try {
connection.close();
} catch (IOException ex) {
}
}
}
private void sendHeader(Writer out, String responseCode, String contentType, int length)
throws IOException {
out.write(responseCode + "\r\n");
Date now = new Date();
out.write("Date: " + now + "\r\n");
out.write("Server: JHTTP 2.0\r\n");
out.write("Content-length: " + length + "\r\n");
out.write("Content-type: " + contentType + "\r\n\r\n");
out.flush();
}
}
I searched for network programming, http, and Java socket programming, and also looked for books, so I think I know the concept, but I don't know what to do to convert it into java code. Even after searching, I can't find the code for the host header in the problem, so I'm at a loss.
Midterm review this week, no progress..
My stuck point
- I don't even know where the host header is and I don't know how to fix it.
- I don't know how to set up the config file in problem #2
Is there any way to implement it in code? And how do developers transfer and use the concepts that came out of the search into code? I don't know what to do if searching doesn't turn up any suitable code.