Download a PDF of this article
Java 18’s Simple Web Server is a minimal HTTP static file server that was added in JEP 408 to the jdk.httpserver
module. It serves a single directory hierarchy, and it serves only static files over HTTP/1.1; dynamic content and other HTTP versions are not supported.
The web server’s specification is informed by the overarching goal of making the JDK more approachable. The server is an out-of-the-box tool with easy setup and minimal functionality that lets you hit the ground running and focus on the task at hand. The simplistic design also avoids any confusion with feature-rich or commercial-grade servers—after all, far better alternatives exist for production environments, and the Simple Web Server is not the right choice in such cases. Instead, this server shines in the context of prototyping, ad hoc coding, and testing.
The server supports only the HEAD
and GET
request methods; any other requests receive either a 501 - Not Implemented
or a 405 - Not Allowed
response. HEAD
and GET
requests are handled as follows:
The jwebserver
tool comes with the following usage options:
jwebserver [-b bind address] [-p port] [-d directory]
[-o none|info|verbose] [-h to show options]
[-version to show version information]
Each option has a short and a long version, and there are conventional options for printing the help message and the version information. Here are the usage options.
-h
or -?
or --help
: Prints the 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). For all interfaces, use -b 0.0.0.0
or -b ::
.-d dir
or --directory dir
: Specifies the directory to serve. The default is the current directory.-o level
or --output level
: Specifies the output format. The levels are none
, info
, and verbose
. The default is info
.-p port
or --port port
: Specifies the port to listen on. The default is 8000
.-version
or --version
: Prints the Simple Web Server’s version information and exits.The following command starts the Simple Web Server:
$ jwebserver
By default, the server binds to the loopback address and port 8000 and serves the current working directory. If startup is successful, the server runs in the foreground and prints a message to System.out
listing the local address and the absolute path of the directory being served, such as /cwd
. For 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/
You can change the default configuration by using the respective options. For example, here is how to bind the Simple Web Server to all interfaces.
$ 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: This command makes the server accessible to all hosts on the network. Do not do this unless you are sure the server cannot leak any sensitive information.
As another example, here is how to run the server on port 9000.
$ jwebserver -p 9000
By default, every request is logged on the console. The output looks like the following:
127.0.0.1 - - [10/Feb/2021:14:34:11 +0000] "GET /some/subdirectory/ HTTP/1.1" 200 –
You can change the logging output with the -o
option. The default setting is info
. The verbose
setting additionally includes the request and response headers as well as the absolute path of the requested resource.
Once it is started successfully, the Simple Web Server runs until it is stopped. On UNIX platforms, the server can be stopped by sending it a SIGINT signal, which is done by pressing Ctrl+C
in a terminal window.
The descriptions above about how to start, configure, and stop the server capture the full extent of the functionality of jwebserver
. The server is minimal yet configurable enough to cover common use cases in web development and web services testing, as well as for file sharing or browsing across systems.
While the jwebserver
tool certainly comes in handy in many scenarios, what if you want to use the components of the Simple Web Server with existing code or further customize them? That’s where a set of new API points come in.
To bridge the gap between the simplicity of the command-line tool and the write-everything-yourself approach of the com.sun.net.httpserver
API, JEP 408 introduces a new set of API points for server creation and customization. (The com.sun.net.httpserver
package has been included in the JDK since 2006.)
The new class SimpleFileServer
offers the key components of the server via three static methods. These methods allow you to retrieve a server instance, a file handler, or an output filter in a straightforward fashion and then custom tailor or combine those functions with existing code as needed.
Retrieving a server instance. The createFileServer
method returns a static file server that’s configured with a bind address and port, a root directory to be served, and an output level. The returned server can be started or configured further, as follows.
Note: The source code examples in this article use jshell
, Java’s convenient read-eval-print loop (REPL) shell.
jshell> import com.sun.net.httpserver.*;
jshell> var server = SimpleFileServer.createFileServer(new InetSocketAddress(8080),
...> Path.of("/some/path"), OutputLevel.VERBOSE);
jshell> server.start()
Retrieving a file handler instance. The createFileHandler
method returns a file handler that serves a given root directory that can be added to a new or existing server. Note the overloaded HttpServer::create
method, which is a nice addition to the API that allows you to initialize a server with a handler in one call.
jshell> var handler = SimpleFileServer.createFileHandler(Path.of("/some/path"));
jshell> var server = HttpServer.create(new InetSocketAddress(8080),
...> 10, "/somecontext/", handler);
jshell> server.start();
Retrieving an output filter. The createOutputFilter
method takes an output stream and an output level and returns a logging filter that can 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();
Now that the server components can easily be retrieved, the Simple Web Server team wanted to enhance the composability of the existing com.sun.net.httpserver
API. In particular the team doubled down on the creation and combination of handlers, which are at the core of the request handling logic.
For this, the team introduced the HttpHandlers
class, which comes with two new methods.
HttpHandlers::of
returns a canned response handler with fixed state, namely a status code, a set of headers, and a response body.HttpHandlers::handleOrElse
combines two handlers on a condition.Here’s an example of how they can be used.
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 canned response handler that always returns a status code of 200, the given header, and the content of a given JSON file as the response body. The notAllowedHandler
, on the other hand, always returns a 405 response. Based on the predicate, the combined handler checks the request method of an incoming request and forwards the request to the appropriate handler.
Taken together, these two new methods make a neat couple for writing custom-tailored logic for request handling and, thus, they can help you tackle various testing and debugging scenarios.
Speaking of testing and debugging, there are other times when you might want to inspect and potentially adapt certain properties of a request before handling it.
To support this, you can use Filter.adaptRequest
, a method that returns a preprocessing filter that can read and optionally change the URI, the method, or the headers of a request.
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();
In the example above, the filter adds a Foo
header to each incoming request before the someHandler
gets a go at it. This functionality enables the straightforward adaptation and extension of an existing handler.
With these new API points at hand, other less-obvious yet interesting applications for the Simple Web Server are within reach. For example, the Simple File Server API can be used for creating an in-memory file server, which serves a .zip
file system or a Java runtime directory. For details and code snippets of these and other examples, see my recent article, “Working with the Simple Web Server.”
The Simple Web Server was designed to facilitate prototyping, debugging, and testing tasks, and I hope the combination of a minimal command-line tool and a flexible set of API points achieves this goal. The jwebserver
tool brings basic server functionality to the command line, while the Simple Web Server API points offer fully customizable server components that can help tackle less-common scenarios and corner cases.
Julia Boes is an OpenJDK developer in Oracle’s Java Platform Group. She holds a master’s degree in linguistics and computer science and works in the areas of core libraries, networking, and serialization. Her other passions are the outdoors, gardening, and sports.
Next Post