HTTP server from scratch: Modularization

This is a continuation in the series of post to build a static HTTP server from scratch. Previous post about Unit testing can be found here.

In this post we are going to refactor the code a little bit to make it somewhat more modular and configurable. Modular in the sense, break out from a single class into smaller classes based on functionality. Finally remove hard-coded configurations.

Break it down

At this point our entire code resides in one class HttpServer. We will break this down into two, an Interface Http and HttpRequestHandler. The interface Http is going to hold some of the constants like, status codes and header names and each instance of HttpRequestHandler will service one incoming request. After the refactoring our main class HttpServer will read the server configuration, based on the configuration create a ServerSocket and listen to requests. When a request arrives, creates an instance of HttpRequestHandler and spawn a new thread to process the request.

Default configuration

Like any configurable application, our server will also have a default set of configuration and user can override them using command line arguments. For default configuration we will create a server.properties file with the following contents in the resources directory.

# default server port
server.port=8080
# default hostname
server.name=localhost
# default server root
server.root=server
# Server string
server.response.version=Http Server v1.0

We will move all the “loose” HTML files into a directory called server in the root of the repository. This directory will act as our fallback document root. Document root is the directory from which requests will be served.

Constants

Next we will create our Interface Http which will define all the constants that we need. Inside this interface we will define three other interfaces Status, Method and Header.

Request Handler

Each new request will be served by a new instance of HttpRequestHandler. Create this class and move all the logic for the moment into the run method. To help us serve a request we define four helper methods parseRequestHeaders, readFile, writeResponseHeaders, and log as shown below.

private Map<String, String> parseRequestHeaders(BufferedReader in) throws IOException {
Map<String, String> headers = new HashMap<>();
String line;
while ((line = in.readLine()) != null && !line.isEmpty()) {
int idx = line.indexOf(':');
if (idx > 0) {
headers.put(line.substring(0, idx).toLowerCase(), line.substring(idx + 1).trim());
}
}
return headers;
}
private byte[] readFile(File file) throws IOException {
byte[] res;
try (FileInputStream fis = new FileInputStream(file)) {
int length = (int) file.length();
res = new byte[length];
//noinspection ResultOfMethodCallIgnored
fis.read(res, 0, length);
}
return res;
}
private void writeResponseHeaders(PrintWriter out, String protocol, String status, String date, int length) {
out.println(protocol + status);
out.println(SERVER + config.getProperty(SERVER_VERSION_PARAM));
out.println(DATE + date);
out.println(CONTENT_TYPE + "text/html; charset=utf-8");
out.println(CONTENT_LENGTH + length);
out.println(CONNECTION + "close");
out.println();
out.flush();
}
private void log(InetAddress remoteAddress, String date, String request, String status, String ua) {
logger.printf("%s [%s] \"%s\"%s %s\n", remoteAddress.getHostAddress(), date, request, status, ua);
}

HTTP header Connection

Most browsers will send HTTP header Connection: keep-alive. This is to use the same connection to request multiple resources. A quick way to speed up page load performance. Since our server only handles single resource request per connection, in our response we will set the header Connection: close to indicate that we are closing this connection.

Next steps

Code up to this point can be found on github branch step-2. After this we will refactor to support new config property web.root. Also the run method’s try/catch block is very big, that will also be broken down into finer chunks for better error handling to send a 500 Internal Server Error when an exceptions occurs.

One thought on “HTTP server from scratch: Modularization

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s