[Java] Create a simple web server with the Java standard library com.sun.net.httpserver

4 minute read

Overview

–Create a simple web server sample using the Java standard library com.sun.net.httpserver package

What is com.sun.net.httpserver?

com.sun.net.httpserver is a Java package that allows you to build an HTTP server.

com.sun.net.httpserver (Java SE 14 & JDK 14)

Provides a simple and advanced HTTP server API that can be used to build built-in HTTP servers. Both “HTTP” and “HTTPS” are supported. The API provides some implementations of RFC 2616 (HTTP 1.1) and RFC 2818 (HTTP over TLS). HTTP functionality not provided by this API can be implemented in your application code using the API.

The programmer must implement the HttpHandler interface. This interface provides a callback that is called to handle incoming requests from clients. The HTTP request and its response are called exchanges. HTTP exchanges are represented by the HttpExchange class. The HttpServer class is used to wait for incoming TCP connections and dispatch requests for these connections to handlers registered on the server.

Note that the com.sun.net.httpserver package belongs to the jdk.httpserver module.

Overview \ (Java SE 14 & JDK 14 )

The Java Development Kit (JDK) API is JDK-specific and may not be available in all implementations of the Java SE platform. These APIs are in modules whose names start with jdk.

This environment

  • macOS 10.15.5 Catalina
  • Java 14 (AdoptOpenJDK 14.0.1)
$ javac --version
javac 14.0.1

$ java --version
openjdk 14.0.1 2020-04-14
OpenJDK Runtime Environment AdoptOpenJDK (build 14.0.1+7)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 14.0.1+7, mixed mode, sharing)

Web server sample code

Save the following source code with the file name MyServer.java.

MyServer.java


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

import java.nio.charset.StandardCharsets;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

public class MyServer {

  public static void main(String args[]) throws IOException {
    //Start HTTP server
    int port = 8000;
    HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
    server.createContext("/", new MyHandler());
    System.out.println("MyServer wakes up: port=" + port);
    server.start();
  }

  //Handler called to handle HTTP request
  private static class MyHandler implements HttpHandler {

    //Handle HTTP requests
    public void handle(HttpExchange t) throws IOException {

      System.out.println("**************************************************");

      //Get the start line
      String startLine =
        t.getRequestMethod() + " " +
          t.getRequestURI().toString() + " " +
          t.getProtocol();
      System.out.println(startLine);

      //Get request header
      Headers reqHeaders = t.getRequestHeaders();
      for (String name : reqHeaders.keySet()) {
        System.out.println(name + ": " + reqHeaders.getFirst(name));
      }

      //Get request body
      InputStream is = t.getRequestBody();
      byte[] b = is.readAllBytes();
      is.close();
      if (b.length != 0) {
        System.out.println(); //Blank line
        System.out.println(new String(b, StandardCharsets.UTF_8));
      }

      //Build a response body
      // (Here we have Switch Expressions officially introduced from Java 14
      //Try using the here-document Text Blocks feature that can be used as a preview feature in Java 14)
      String resBody = switch (t.getRequestURI().toString()) {
        case "/hello" -> "{\"message\": \"Hello, World!\"}";
        case "/foobar" -> """
          {
            "foo": "bar",
            "Huh": "Bah"
          }""";
        default -> "{}";
      };

      // Content-Set response headers other than Length
      Headers resHeaders = t.getResponseHeaders();
      resHeaders.set("Content-Type", "application/json");
      resHeaders.set("Last-Modified",
        ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.RFC_1123_DATE_TIME));
      resHeaders.set("Server",
        "MyServer (" +
          System.getProperty("java.vm.name") + " " +
          System.getProperty("java.vm.vendor") + " " +
          System.getProperty("java.vm.version") + ")");

      //Send response header
      int statusCode = 200;
      long contentLength = resBody.getBytes(StandardCharsets.UTF_8).length;
      t.sendResponseHeaders(statusCode, contentLength);

      //Send response body
      OutputStream os = t.getResponseBody();
      os.write(resBody.getBytes());
      os.close();
    }
  }
}

compile

Compile with the javac command.
Since we are using Text Blocks that can be used as a preview function in Java 14, it is necessary to specify –enable-preview –release 14 as an option.

$ javac --enable-preview --release 14 MyServer.java
Caution:MyServer.java uses the preview language feature.
Caution:Detail is,-Xlint:Please recompile with the preview option.

Compiling creates a MyServer.class file.

$ ls
MyServer.class  MyServer.java

Start the web server

Specifying MyServer in the java command starts the web server.

$ java --enable-preview MyServer
MyServer wakes up: port=8000

GET request to web server

Make an HTTP GET request to / foobar with the curl command.
You can see that JSON is returned as a response.

$ curl -v http://localhost:8000/foobar
*   Trying ::1:8000...
* Connected to localhost (::1) port 8000 (#0)
> GET /foobar HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.70.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: MyServer (OpenJDK 64-Bit Server VM AdoptOpenJDK 14.0.1+7)
< Date: Tue, 23 Jun 2020 23:48:57 GMT
< Last-modified: Tue, 23 Jun 2020 23:48:57 GMT
< Content-type: application/json
< Content-length: 40
< 
{
  "foo": "bar",
  "Huh": "Bah"
* Connection #0 to host localhost left intact
}

The MyServer program outputs HTTP GET request information.

**************************************************
GET /foobar HTTP/1.1
Accept: */*
Host: localhost:8000
User-agent: curl/7.70.0

POST request to web server

Make an HTTP POST request for JSON data to / hello with the curl command.
You can see that JSON is returned as a response.

$ curl -v -H "Content-Type: application/json" -d '{"foobar":"Fuba"}' http://localhost:8000/hello
*   Trying ::1:8000...
* Connected to localhost (::1) port 8000 (#0)
> POST /hello HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.70.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 25
> 
* upload completely sent off: 25 out of 25 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: MyServer (OpenJDK 64-Bit Server VM AdoptOpenJDK 14.0.1+7)
< Date: Wed, 24 Jun 2020 20:31:18 GMT
< Last-modified: Wed, 24 Jun 2020 20:31:18 GMT
< Content-type: application/json
< Content-length: 28
< 
* Connection #0 to host localhost left intact
{"message": "Hello, World!"}

The MyServer program outputs HTTP POST request information.
The JSON specified in the request body is output.

**************************************************
POST /hello HTTP/1.1
Accept: */*
Host: localhost:8000
User-agent: curl/7.70.0
Content-type: application/json
Content-length: 25

{"foobar":"Fuba"}

Reference material

Tags:

Updated: