How to create HTTP server in Java to serve static resources

Introduction

Here I will show you how to create HTTP server in Java to serve static resources using sun’s HttpServer. An HTTP Server is bound to an IP address and port number and listens for incoming requests and returns responses to clients. Simple http server is flexible to be added into complex projects for rendering HTML elements or serving as a backend server.

An HTTP server in Java can be created in four steps:

  1. Create HttpServer object
  2. Attach one or more HttpHandler objects to HttpServer object
  3. Create class to implement HttpHandler to handle GET/POST requests
  4. Start HttpServer

You can find more details about sun’s HttpServer on Oracle docs.

Here I will use a sample HTML project to serve from my created http server. The sample website is already included into downloaded zip file.

Usage Details

Usage Details of the HTTP server:

  1. Extract the zip folder into your desired location
  2. Navigate to the bin directory using cmd or console window
  3. Execute the start.bat file
  4. Now point to the URL http://localhost:8000 in browser
  5. You can stop server by pressing Ctrl+c key from your keyboard.

If you want to start the server from Eclipse IDE:

  • Comment the below piece of code in the main class
/*if (args.length == 0) {
			System.out.println("Usage: java -jar <jar name> <server home> <port>");
			System.exit(0);
		}

		serverHome = args[0];
		port = args.length == 1 ? ServerConstant.DEFAULT_PORT : Integer.parseInt(args[1]);*/
  • Add below piece of code
port = 8000;
serverHome = "C:\\workspace\\httpserver";
  • Make sure to change the serverHome according to your project root directory.
  • Create webapp directory under project root directory.
  • Put the HTML content into this webapp directory.
  • Run the main class.
  • Hit the URL http://localhost:8000 to see the results.

Change Port

If you want to change the default port (for example, 9000) then you can modify the start.bat file to pass port number as a second parameter. For example,

java -jar %ServerHome%/lib/myserver.jar %ServerHome% 9000

Source Content

The lib directory contains the jar file that contains required Java class files to serve your static resources.

The webapp directory contains HTML project including HTML, css, images etc.

Now you will see pages are appearing in the browser. Now you can navigate to different menu on browser given serving from the sample HTML project.

You can also download the sample HTML project from here.

You can download the source entire source code of the project from below link.

Download

Here you need to focus on the class ServerResourceHandler that handles actually static resources from your given root directory of the website.

The source of the ServerResourceHandler is given below, you can find rest of the source code in the downloaded zip file.

In the below class we have implemented the HttpHandler as we are using Sun’s HttpServer and we have also overridden the handle() method to serve our static resources.

Sun’s HttpServer provides HttpExchange with very useful information for serving our content from directories.

We pass three parameters to our constructor – root directory of the web application, whether content should be compressed before sending to the client or browser, whether content should be cacheable for subsequent request for faster processing.

package com.roytuts.httpserver.handler;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import java.util.zip.GZIPOutputStream;

import com.roytuts.httpserver.constant.ServerConstant;
import com.roytuts.httpserver.enums.HttpMethod;
import com.roytuts.httpserver.utils.ServerUtil;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;

public class ServerResourceHandler implements HttpHandler {

	private static final Logger LOGGER = Logger.getLogger(ServerResourceHandler.class.getName());

	private final String pathToRoot;
	private final boolean gzippable;
	private final boolean cacheable;
	private final Map<String, Resource> resources = new HashMap<>();

	public ServerResourceHandler(String pathToRoot, boolean gzippable, boolean cacheable) throws IOException {
		this.pathToRoot = pathToRoot.endsWith(ServerConstant.FORWARD_SINGLE_SLASH) ? pathToRoot
				: pathToRoot + ServerConstant.FORWARD_SINGLE_SLASH;
		this.gzippable = gzippable;
		this.cacheable = cacheable;

		File[] files = new File(pathToRoot).listFiles();
		if (files == null) {
			throw new IllegalStateException("Couldn't find webroot: " + pathToRoot);
		}
		for (File f : files) {
			processFile("", f, gzippable);
		}
	}

	@Override
	public void handle(HttpExchange httpExchange) throws IOException {
		String requestPath = httpExchange.getRequestURI().getPath();

		LOGGER.info("Requested Path: " + requestPath);

		serveResource(httpExchange, requestPath);
	}

	private class Resource {
		public final byte[] content;

		public Resource(byte[] content) {
			this.content = content;
		}
	}

	private void processFile(String path, File file, boolean gzippable) throws IOException {
		if (!file.isDirectory()) {
			resources.put(path + file.getName(), new Resource(readResource(new FileInputStream(file), gzippable)));
		}

		if (file.isDirectory()) {
			for (File sub : file.listFiles()) {
				processFile(path + file.getName() + ServerConstant.FORWARD_SINGLE_SLASH, sub, gzippable);
			}
		}
	}

	private byte[] readResource(final InputStream in, final boolean gzip) throws IOException {
		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		OutputStream gout = gzip ? new GZIPOutputStream(bout) : new DataOutputStream(bout);
		byte[] bs = new byte[4096];
		int r;
		while ((r = in.read(bs)) >= 0) {
			gout.write(bs, 0, r);
		}
		gout.flush();
		gout.close();
		in.close();
		return bout.toByteArray();
	}

	private void serveResource(HttpExchange httpExchange, String requestPath) throws IOException {
		requestPath = requestPath.substring(1);
		requestPath = requestPath.replaceAll(ServerConstant.FORWARD_DOUBLE_SLASH, ServerConstant.FORWARD_SINGLE_SLASH);
		if (requestPath.length() == 0) {
			requestPath = "index.html";
		}
		serveFile(httpExchange, pathToRoot + requestPath);
	}

	private void serveFile(HttpExchange httpExchange, String resourcePath) throws IOException {
		File file = new File(resourcePath);
		if (file.exists()) {
			InputStream in = new FileInputStream(resourcePath);

			Resource res = null;

			if (cacheable) {
				if (resources.get(resourcePath) == null) {
					res = new Resource(readResource(in, gzippable));
				} else {
					res = resources.get(resourcePath);
				}
			} else {
				res = new Resource(readResource(in, gzippable));
			}

			if (gzippable) {
				httpExchange.getResponseHeaders().set(ServerConstant.CONTENT_ENCODING, ServerConstant.ENCODING_GZIP);
			}

			String mimeType = ServerUtil.getFileMime(resourcePath);
			writeOutput(httpExchange, res.content.length, res.content, mimeType);
		} else {
			showError(httpExchange, 404, "The requested resource was not found on server");
		}
	}

	private void writeOutput(HttpExchange httpExchange, int contentLength, byte[] content, String contentType)
			throws IOException {
		if (HttpMethod.HEAD.getName().equals(httpExchange.getRequestMethod())) {
			Set<Map.Entry<String, List<String>>> entries = httpExchange.getRequestHeaders().entrySet();
			String response = "";
			for (Map.Entry<String, List<String>> entry : entries) {
				response += entry.toString() + "\n";
			}
			httpExchange.getResponseHeaders().set(ServerConstant.CONTENT_TYPE, ServerConstant.TEXT_PLAIN);
			httpExchange.sendResponseHeaders(200, response.length());
			httpExchange.getResponseBody().write(response.getBytes());
			httpExchange.getResponseBody().close();
		} else {
			httpExchange.getResponseHeaders().set(ServerConstant.CONTENT_TYPE, contentType);
			httpExchange.sendResponseHeaders(200, contentLength);
			httpExchange.getResponseBody().write(content);
			httpExchange.getResponseBody().close();
		}
	}

	private void showError(HttpExchange httpExchange, int respCode, String errDesc) throws IOException {
		String message = "HTTP error " + respCode + ": " + errDesc;
		byte[] messageBytes = message.getBytes(ServerConstant.ENCODING_UTF8);

		httpExchange.getResponseHeaders().set(ServerConstant.CONTENT_TYPE, ServerConstant.TEXT_PLAIN);
		httpExchange.sendResponseHeaders(respCode, messageBytes.length);

		OutputStream os = httpExchange.getResponseBody();
		os.write(messageBytes);
		os.close();
	}

}

Testing the Server

The home page on browser using URL http://localhost:8000 you will find similar to the below image:

create HTTP server in Java to serve static resources

That’s all. Hope you got idea how to create http server in Java to serve static resources.

Improvements

There are many areas where you can improve:

  1. This supports only single web application, so you can create multiple contexts to serve from multiple web applications
  2. This does not support Query Parameters, so you can improve code to support Query Parameters
  3. This does not handle most of the http status codes, you can improve
  4. This does not support all four Content-Encoding, you can improve on this

Need to deploy multiple websites? Please get in touch with me.

Thanks for reading.

Leave a Comment