News and views from members of the Java team at Oracle
Java 18's Simple Web Server is a minimal HTTP static file server added as a jdk.httpserver module in JEP 408. This server can only publish static files in a single directory hierarchy using HTTP/1.1. Dynamic content and other HTTP versions are not supported.
This web server's specifications are based on the overarching goal of making the JDK more user-friendly. It includes only the bare minimum of features, making it an easy-to-configure and immediately usable tool, allowing you to quickly focus on the task at hand. Its simple design also ensures it won't be confused with high-performance, commercial-grade servers. In fact, far superior servers exist for production environments, and Simple Web Server shouldn't be used for such purposes. However, it excels at prototyping, testing code on the fly, and other similar tasks.
This server only supports the HEAD and GET request methods. If it receives any other request, it will return 501 - Not Implemented or 405 - Not Allowed. HEAD and GET requests are processed as follows:
If the requested resource is a file, provide its contents.
If the requested resource is a directory and an index file exists within it, provide the contents of that index file.
Otherwise, return a list of directories.
The jwebserver tool supports the following options:
jwebserver [-b bind address] [-p port] [-d directory]
[-o none|info|verbose] [-h to show options]
[-version to show version information]
All options have short and long versions, as well as idiomatic options that display help messages and version information. The available options are as follows:
-h or -?, --help: Displays a help message and exits.
-b addr or --bind-address addr: Specifies the address to bind to. The default is 127.0.0.1 or ::1 (loopback). -b 0.0.0.0 or -b :: can be used to bind to any interface.
-d dir or --directory dir: Specify the directory to make public. The default is the current directory.
-o level or --output level: Specifies the output format. The level can be one of none, info, or verbose. The default is info.
-p port or --port port: Specify the port to listen on. The default is 8000.
-version or --version: Display the Simple Web Server version information and exit.
Running the following command will start Simple Web Server.
$ jwebserver
By default, the loopback address and port 8000 are bound, and the current directory is exposed. Upon successful startup, the server runs in the foreground and displays a message to System.out containing the local address and the absolute path of the directory to be exposed (e.g., /cwd). Here is an example:
$ jwebserver
Binding to loopback by default. For all interfaces use "-b 0.0.0.0" or "-b ::".
Serving /cwd and subdirectories on 127.0.0.1 port 8000
URL http://127.0.0.1:8000/
Each option allows you to change the default configuration. For example, to bind Simple Web Server to all interfaces, do the following:
$ jwebserver -b 0.0.0.0
Serving /cwd and subdirectories on 0.0.0.0 (all interfaces) port 8000
URL http://123.456.7.891:8000/
Warning: Using this command will allow all hosts on the network to access this server. Do this only if you are certain that the server will not leak any sensitive information.
Let's look at another example. To run a server on port 9000, you would do the following:
$ jwebserver -p 9000
By default, all requests are displayed as logs in the console. The output will look like this:
127.0.0.1 - - [10/Feb/2021:14:34:11 +0000] "GET /some/subdirectory/ HTTP/1.1" 200
This log output can be optionally modified with -o. The default setting is info. Setting it to verbose will also display request and response header information, as well as the absolute path of the requested resource.
Once Simple Web Server starts successfully, it continues to run until stopped. On UNIX platforms, you can stop the server by sending a SIGINT signal. To do this, press [Ctrl]+[C] in a terminal window.
We've covered how to start, configure, and stop the server, and this jwebserver tool is the complete overview of its functionality. While it only has minimal features, it's sufficient for common web development use cases and web service testing. Furthermore, it allows for file sharing and viewing between systems.
While the jwebserver tool is useful in a variety of situations, there may be times when you want to use Simple Web Server components from existing code or further customize the server. That's where the new set of API points comes in.
com.sun.net.httpserver API PointJEP 408 introduces a set of new API points in com.sun.net.httpserver that can be used to create and customize servers, bridging the gap between the simplicity of this command-line tool and the API's "write it all yourself" approach (com.sun.net.httpserver packages are included in JDKs from 2006 onwards).
The new SimpleFileServer class, which is the main component of this server, has three static methods. These three methods allow you to easily obtain the server instance, file handler, and output filter. Then, you can customize each of these functions as needed or combine them with existing code.
The method for obtaining a server instance createFileServer returns a static file server. You specify the address and port to bind to, the root directory to expose, and the output level. The returned server can then be started and further configured. Here's an example:
Note: The source code examples in this article use Java's convenient Read-Eval-Print Loop (REPL) shell, jshell.
jshell> import com.sun.net.httpserver.*;
jshell> var server = SimpleFileServer.createFileServer(new InetSocketAddress(8080),
...> Path.of("/some/path"), OutputLevel.VERBOSE);
jshell> server.start()
The File:GetHandlerInstance method createFileHandler returns a file handler that exposes the specified root directory. This handler can be added to new or existing servers. The great thing here is that overloaded HttpServer::create methods have been added to the API, allowing you to initialize a server containing file handlers in a single call.
jshell> var handler = SimpleFileServer.createFileHandler(Path.of("/some/path"));
jshell> var server = HttpServer.create(new InetSocketAddress(8080),
...> 10, "/somecontext/", handler);
jshell> server.start();
The Retrieve Output Filter createOutputFilter method accepts an output stream and output level and returns a logging filter. This filter can then be added to an existing server.
jshell> var filter = SimpleFileServer.createOutputFilter(System.out,
...> OutputLevel.INFO);
jshell> var server = HttpServer.create(new InetSocketAddress(8080),
...> 10, "/somecontext/", new SomeHandler(), filter);
jshell> server.start();
Having made it easy to retrieve server components, the Simple Web Server team then wanted to enhance the integration capabilities of existing com.sun.net.httpserver APIs. They particularly focused on the ability to create and combine handlers, which form the core of the request processing logic.
To do this, a HttpHandlers class was introduced. This class has two new methods.
HttpHandlers::of: It returns a response handler that combines a specific set of conditions (status code, a series of headers, and the response body).
HttpHandlers::handleOrElse: Combine two handlers depending on the conditions.
Next, here are some examples of how to use it.
jshell> Predicate<Request> IS_GET = r -> r.getRequestMethod().equals("GET");
jshell> var jsonHandler = HttpHandlers.of(200,
...> Headers.of("Content-Type", "application/json"),
...> Files.readString(Path.of("some.json")));
jshell> var notAllowedHandler = HttpHandlers.of(405, Headers.of("Allow", "GET"), "");
jshell> var handler = HttpHandlers.handleOrElse(IS_GET, jsonHandler, notAllowedHandler);
In the example above, the jsonHandler is a response handler that always returns a status code of 200, the specified headers, and the response body (the contents of the specified JSON file). The other notAllowedHandler always returns a 405 response. The combined handler checks the request method of an incoming request based on conditions and forwards the request to the appropriate handler.
Therefore, these two new methods are useful when writing custom logic for request processing. As such, they can be used for various tests and debugging.
When testing or debugging, you may want to examine and address specific properties of a request before processing it.
To achieve this, you can use Filter.adaptRequest, a method that returns a filter for pre-processing. This filter can read the request's URI, method, and headers, and you can modify them as needed.
jshell> var filter = Filter.adaptRequest("Add Foo header",
...> request -> request.with("Foo", List.of("Bar")));
jshell> var server = HttpServer.create(new InetSocketAddress(8080), 10, "/", someHandler,
...> filter);
jshell> server.start();
The filter in the example above adds a Foo header to all incoming requests before passing them to someHandler. This feature makes it easy to support and extend the functionality of existing handlers.
With these new API points available in Simple Web Server, you can access other less obvious but interesting applications. For example, you can use the Simple File Server API to create an in-memory .zip file server or expose a file system or Java runtime directory. For more details and code snippets of such servers, see my latest article, "Working with the Simple Web Server."
Simple Web Server is designed to assist with prototyping, debugging, and testing tasks, and you can expect it to achieve this goal through a combination of minimal command-line tools and a flexible set of API points. The jwebserver tool provides basic server functionality via the command line, while the Simple Web Server API points offer fully customizable server components, allowing for less common use cases.
This article was originally published at https://blogs.oracle.com/javamagazine/post/java-18-simple-web-server.