The unofficial com.sun packages which is still part of the main JDK, include a few convenience classes for running a HTTP server. However, since these are not part of the official API, there’s typically a warning in the IDEs not to use them. The annotation @SuppressWarnings(“restriction”) can also be used to ignore this warning. Besides that, they will work fine.

Here’s what it takes to start the server on a given port, and serve static files from a specific directory tree:

  void start() throws IOException {
    server = HttpServer.create(new InetSocketAddress(PORT), 0);

    server.createContext("/static", new StaticFileHandler(BASEDIR));

    server.start();
  }

The second argument to the create() method, which is currently 0, is the number of queued incoming queries. Setting a higher number here will allow the server to handle more parallel incoming requests, however potentially at the expense of overall throughput.

The start() is non-blocking, so a typically server application would have to go into an internal loop.

As for the handler, it listens to requests where the path of the URI starts with the specified string, in this case “/static”. It can then use the request URI to map this to hard-coded files, or files with the same name as in this example:

  public void handle(HttpExchange ex) throws IOException {
    URI uri = ex.getRequestURI();
    String name = new File(uri.getPath()).getName();
    File path = new File(baseDir, name);

    Headers h = ex.getResponseHeaders();
    // Could be more clever about the content type based on the filename here.
    h.add("Content-Type", "text/html");

    OutputStream out = ex.getResponseBody();

    if (path.exists()) {
      ex.sendResponseHeaders(200, path.length());
      out.write(Files.readAllBytes(path.toPath()));
    } else {
      System.err.println("File not found: " + path.getAbsolutePath());

      ex.sendResponseHeaders(404, 0);
      out.write("404 File not found.".getBytes());
    }

    out.close();
  }

Finally, to test the running server, here’s two unit tests. The first downloads a file which exists, and prints its lines. The second requests a file which does not exist. Notice that the 404 return code passed from sendResponseHeaders() in the handler, is enough to make the openStream() call throw a FileNotFoundException on the client side.

  //@Test
  public void disabled_testDownload() throws IOException {
    server.start();

    URL url = new URL("http://localhost:9999/static/test.txt");
    BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
    in.lines().forEach(System.out::println);
    in.close();
  }

  @Test(expected = FileNotFoundException.class)
  public void testFilenNotFound() throws IOException {
    server.start();

    URL url = new URL("http://localhost:9999/static/not_found");
    url.openStream();
  }

Here’s the main class:

SimpleHttpServer.java
GitHub Raw
/* Copyright rememberjava.com. Licensed under GPL 3. See http://rememberjava.com/license */
package com.rememberjava.http;

import java.io.IOException;
import java.net.InetSocketAddress;

import com.sun.net.httpserver.HttpServer;

@SuppressWarnings("restriction")
public class SimpleHttpServer {

  private static final String BASEDIR = "com/rememberjava/http";

  private static final int PORT = 9999;

  private HttpServer server;

  public static void main(String[] args) throws Exception {
    SimpleHttpServer server = new SimpleHttpServer();
    server.start();
  }

  void start() throws IOException {
    server = HttpServer.create(new InetSocketAddress(PORT), 0);

    server.createContext("/static", new StaticFileHandler(BASEDIR));

    server.start();
  }

  public void stop() {
    server.stop(0);
  }
}

And here’s the Handler for static files:

StaticFileHandler.java
GitHub Raw
/* Copyright rememberjava.com. Licensed under GPL 3. See http://rememberjava.com/license */
package com.rememberjava.http;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.nio.file.Files;

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;

@SuppressWarnings("restriction")
public class StaticFileHandler implements HttpHandler {

  private final String baseDir;

  public StaticFileHandler(String baseDir) {
    this.baseDir = baseDir;
  }

  @Override
  public void handle(HttpExchange ex) throws IOException {
    URI uri = ex.getRequestURI();
    String name = new File(uri.getPath()).getName();
    File path = new File(baseDir, name);

    Headers h = ex.getResponseHeaders();
    // Could be more clever about the content type based on the filename here.
    h.add("Content-Type", "text/html");

    OutputStream out = ex.getResponseBody();

    if (path.exists()) {
      ex.sendResponseHeaders(200, path.length());
      out.write(Files.readAllBytes(path.toPath()));
    } else {
      System.err.println("File not found: " + path.getAbsolutePath());

      ex.sendResponseHeaders(404, 0);
      out.write("404 File not found.".getBytes());
    }

    out.close();
  }

}