Undertow Web Server Tutorial

Jakob Jenkov
Last update: 2023-07-02

Undertow web server is an open source minimalistic Java web server sponsored by Red Hat. Undertow is used inside the JBoss Wildfly application server. Undertow provides both blocking and non-blocking APIs based on Java NIO, and has a compositional design that enables you create your own custom web server by combining special purpose request handlers along with your own custom request handlers.

Undertow is designed to be embeddable, meaning you can create and start an Undertow web server from inside your own Java application. You could actually start multiple Undertow web servers from inside a single Java application.

By minimalistic I mean that it is possible to compile your customized Java application with the embedded Undertow web server - including its JAR file dependencies - into a single standalone fat jar as small as 5-6 MB.

I have used Undertow for several years to run several websites, and it has worked quite well so far.

Undertow Website

You can find Undertow at the Undertow website, here:

Undertow.io

Undertow Examples GitHub Repository

I have published many of the Undertow examples from this tutorial in a GitHub repository so you can see the code in its entirety. Each example tends to be small and isolated, using a minimum of external classes - to keep the code simple for you to read. Here is the Undertow examples GitHub repository:

https://github.com/jjenkov/undertow-examples

Undertow Maven Dependencies

To use Undertow in your Java project you must include the Undertow Maven dependencies in your Maven pom.xml file. Here is how the core Undertow Maven dependency looks:

<dependency>
    <groupId>io.undertow</groupId>
    <artifactId>undertow-core</artifactId>
    <version>2.1.0.Final</version>
</dependency>

Undertow also has a few extra JAR files you can use, if you need the extra functionality they provide. These Undertow JAR files are:

<dependency>
    <groupId>io.undertow</groupId>
    <artifactId>undertow-servlet</artifactId>
    <version>2.1.0.Final</version>
</dependency>
<dependency>
    <groupId>io.undertow</groupId>
    <artifactId>undertow-websockets-jsr</artifactId>
    <version>2.1.0.Final</version>
</dependency>

Undertow Overview

When you use Undertow you create an Undertow server. This server listens on a TCP port for incoming HTTP requests. All incoming HTTP requests are forwarded to a Handler which you set on the Undertow server. How this looks is shown in the next section.

Simple Undertow Application

Here is a simple application that creates an Undertow web server and starts it up:

import io.undertow.Undertow;
import java.io.IOException;

public class UndertowHttpServer {

    public static void main(String[] args) throws IOException {
        System.out.println("Building Undertow server");

        WebHandler webHandler = new WebHandler();

        Undertow.Builder builder = Undertow.builder();
        Undertow undertow = builder
                .addHttpListener(80, "localhost")
                .setHandler(webHandler)
                .build();

        System.out.println("Undertow started");
        undertow.start();
    }
}

Notice the Undertow.Builder class that is used to build the Undertow server instance. Also notice the WebHandler which is the component that receives all the incoming HTTP requests. See the next section for an example of a Handler implementation.

Simple Undertow Handler

Here is a simple Undertow handler which receives a request and sends a simple, hardcoded response back:

import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;

public class WebHandler implements HttpHandler {

    @Override
    public void handleRequest(HttpServerExchange httpServerExchange) throws Exception {
        httpServerExchange
            .getResponseSender()
                .send("<!DOCTYPE html<>html<>body<>h1<Hello World from Undertow>/h1<>/body<>/html>");
    }
}
HTTP Request URI

HTTP Request URI

You can access the HTTP Request URI via the HttpServerExchange object passed as parameter to the HttpHandler's handleRequest() method. Here is an example of accessing the HTTP Request URI:

String uri = httpServerExchange.getRequestURI();

HTTP Request Headers

You can access the HTTP request headers such as the Accept header via the HttpServerExchange object passed as parameter to the HttpHandler's handleRequest() method. Here is an example of accessing the Accept HTTP request header:

import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.HeaderValues;

public class HttpHeadersHandler implements HttpHandler {
    @Override
    public void handleRequest(HttpServerExchange httpServerExchange) throws Exception {

        //obtain header values for the Accept HTTP header
        HeaderValues acceptHeader = httpServerExchange.getRequestHeaders().get("Accept");

        System.out.println("");
        System.out.println("URI: " + httpServerExchange.getRequestURI());
        System.out.println("Accept header values: " + acceptHeader.size());

        //print each value out to System.out
        for(int i=0; i < acceptHeader.size(); i++) {
            System.out.println("" + i + " : " + acceptHeader.get(i));
        }

        httpServerExchange
                .getResponseSender()
                .send("<!DOCTYPE html><html><body><h1>HTTP Accept Headers Printed to System.out</h1></body></html>");

    }
}

String uri = httpServerExchange.getRequestURI();

HTTP Response Headers

It is possible to set the HTTP response headers too, via the HttpServerExchange object passed as parameter to the HttpHandler's handleRequest() method. Here is an example of setting the Content-Type HTTP response header:

String mimeType = "text/html";
httpServerExchange
    .getResponseHeaders()
        .put(HttpString.tryFromString("Content-Type"), mimeType);

HTTP 404 Not Found Handler

It is possible to send an HTTP 404 URI Not Found error code back to the browser, in case your Undertow service cannot find any content matching the incoming URI + parameters. Here is a NotFound404Handler that shows how simple it is to send back an HTTP 404 code:

import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;

public class NotFound404Handler implements HttpHandler {

    @Override
    public void handleRequest(HttpServerExchange httpServerExchange) throws Exception {
        httpServerExchange.setStatusCode(404);
        httpServerExchange.getResponseSender()
            .send("URI not found: " + httpServerExchange.getRequestURI());
    }
}

Routing Undertow Handler

As mentioned earlier, the Undertow takes a simple Handler instance which receives all the incoming HTTP requests to the Undertow server. In order to route the incoming requests to different Handler instances based on the URI of the HTTP request, you have to implement a routing Handler yourself.

Here is a simple example of an Undertow Handler which looks at the URI of the incoming HTTP request, and routes the HTTP request to a Handler matching that URI, by looking up the Handler by URI in a Map:

import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;

import java.util.HashMap;
import java.util.Map;

public class RoutingHandler implements HttpHandler {

    private Map<String, HttpHandler> handlerMap = new HashMap<>();

    public void addHandler(String uri, HttpHandler handler) {
        this.handlerMap.put(uri, handler);
    }

    @Override
    public void handleRequest(HttpServerExchange httpServerExchange) throws Exception {
        String uri = httpServerExchange.getRequestURI();

        HttpHandler handler = this.handlerMap.get(uri);

        if(handler != null) {
            handler.handleRequest(httpServerExchange);
        }

        // else - send back an HTTP 404. See a later example.
    }
}

File Handler

It is of course possible to send back static content from an Undertow handler. Most websites have some amount of static content typically stored in files. Here is an Undertow file handler capable of translating the request URI to a file path in the local file system, load the corresponding file and return it to the browser.

import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.Headers;

import java.io.File;
import java.nio.ByteBuffer;

public class FileHandler implements HttpHandler {

    private String baseDirPath = null;

    public FileHandler(String baseDirPath) {
        this.baseDirPath = baseDirPath;
    }

    @Override
    public void handleRequest(HttpServerExchange httpServerExchange) throws Exception {
        String fileUri  = httpServerExchange.getRequestURI();
        String mimetype = MimeTypeDetector.detectMimeType(fileUri);
        String filePath = (this.baseDirPath + fileUri).replace('/', File.separatorChar);

        try{
            byte[] bytes = FileUtil.loadFile(filePath);

            //Perhaps allocating a new ByteBuffer for every request is not optimal.
            // A single instance could be reused, although it then has to be big enough to hold all files.
            ByteBuffer buffer = ByteBuffer.allocate(bytes.length);

            buffer.clear();
            buffer.put(bytes);
            buffer.flip();

            httpServerExchange.getResponseHeaders().put(Headers.CONTENT_TYPE, mimetype);
            httpServerExchange.getResponseSender().send(buffer);
            httpServerExchange.endExchange();
        } catch(Throwable t) {
            t.printStackTrace();
        }
    }
}

Jakob Jenkov

Featured Videos




















Advertisements

High-Performance
Java Persistence
Close TOC
All Tutorial Trails
All Trails
Table of contents (TOC) for this tutorial trail
Trail TOC
Table of contents (TOC) for this tutorial
Page TOC
Previous tutorial in this tutorial trail
Previous
Next tutorial in this tutorial trail
Next