HTTP server from scratch: Configuration

This post is the conclusion of HTTP server from scratch. Previous post about modularization can be found here.

In this post we will make our webserver somewhat more configurable. We will add one more switch to the to point to a external directory that will be used to serve the documents from. Since it is a good idea to support compression of the documents that we send, we will add support to gzip compression and finally, we will update our build configuration to generate a property distribution.


The new configuration property that we will add is the web.root, default setting will point to server.root, when running it outside of the IDE we will be able to use -Dweb.root=/path/to/web/root.

Before we can do that we have add one more HTTP status code 400 Bad Request. According to the HTTP spec, the resource the user-agent can request should be a absolute path and not a relative path. As a defensive approach we will sanitize the incoming request to contain only absolute paths, otherwise we will return with HTTP status code 400 Bad Request.

So far we have been only serving a single file, index.html from the root of server.root, but a web server should be able to serve other static documents from root and sub-directories. Typically all CSS documents will be located in css and JavaScript in js directories.

Improved logging

We will improve our logging to include the requested resource in the output. To do that we will add an additional String argument resource to the method log. Now we will know what was requested and its corresponding status.

Resolving the requested resource

To help resolve the requested resource, we will add a helper method resolveResource, which will take a String and return a String. In this method we will walk the requested resource path and skipping relative paths like . and ...

Other helper methods

We need other helper methods which will parse the request headers, determine if the User-Agent can support response compression and get the correct type of OutputStream to which we will write the response.

We will also update the existing helper method, writeResponseHeaders to take an additional argument describing the MIME type of the response. Earlier we had hardcoded the MIME type as text/html. To help determine the MIME type of the document, Java already has a static method probeContentType in Files class.

Finally we are in a position to refactor the run method to get to what we set out to achieve in the beginning of the post.

We will not know the response type until we have parsed the headers, so we will not be able to use the try-with-resources for the dataOut OutputStream. So we will have to do it the old fashioned way. After parsing the headers we determine how to respond to the request the corresponding Status code. Finally, we will create either a BufferedOutputStream or GZIPOutputStream depending on what the User-Agent accepts.

Building a distribution

To let gradle know that we have to package the server directory, which is our default server.root, we will update our build.gradle and add the following.

Now if execute gradle distTar or gradle distZip we will get a nice distribution ready tar or zip file.

Code so far

Code for this post and the rest of the posts in the series can be found on GitHub at Branch step-3 contains the modifications from this post.

Leave a Reply

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

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

Google photo

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

Twitter picture

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

Facebook photo

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

Connecting to %s