Configuring Undertow Filters on WildFly

Undertow filters can be used to modify some aspects of an HTTP request. They are commonly used to add or remove some settings from HTTP Headers or to compress the request. In this tutorial we will learn how to create a custom Undertow Filter

As said, out of the box Undertow provides some built-in filters which are substantially the equivalent of former Tomcat Valves. You can find the available filters into the source code of Undertow project: https://github.com/undertow-io/undertow/tree/master/servlet/src/main/java/io/undertow/servlet/handlers

Let's see as an example how to add some simple filters.

You can control the number of concurrent requests by using a connection limit filter, which includes the number of maximum concurrent requests and the number of request allowed to be queued up:

/subsystem=undertow/configuration=filter/connection-limit=mylimit/:add(max-concurrent-requests=25,queue-size=100)

Now you can apply the filter at host level as follows:

/subsystem=undertow/server=default-server/host=default-host/filter-ref= mylimit/:add()

Another example which is worth mentioning is the gzip filter which compresses the response to allow a faster throughput of data:

/subsystem=undertow/configuration=filter/gzip=zipfilter/:add

Then you can apply the filter at host level as follows:

/subsystem=undertow/server=default-server/host=default-host/filter-ref=zipfilter/:add() 

Creating a Custom Undertow Filter

In order to create a custom Undertow, all you have to do is implementing the io.undertow.server.HttpHandler interface and its method handleRequest so that you can capture the incoming HTTP request and pass on to the next Filter:

public class MyHandler implements HttpHandler {

    @Override
    public void handleRequest(HttpServerExchange exchange) throws Exception {
      . . . .
    }
}

Let's try to code a simple Filter which dumps the HostName which requested an HTTP Resource to one Undertow Host:

package com.mastertheboss;

import io.undertow.server.ExchangeCompletionListener;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyHandler implements HttpHandler {

    private static final Logger log = LoggerFactory.getLogger(MyHandler.class);

    private HttpHandler next;

    public MyHandler(HttpHandler next) {
        this.next = next;
    }

    @Override
    public void handleRequest(HttpServerExchange exchange) throws Exception {


    	System.out.println("Received request from " + exchange.getDestinationAddress().getHostName());
        next.handleRequest(exchange);
    }
}

As you can see, the simple logic is contained in the handleRequest method which will eventually call the next handler in the chain.

It's important to note that you shouldn't code any thread blocking procedure in the handleRequest method as this code will be executed by one of the XNIO Worker threads which needs to be reused across the HTTP Requests

Once done, package the class in a JAR file and add it as a module into WildFly:

module add --name=com.mastertheboss.handler --resource=myfilter.jar --dependencies=io.undertow.core,org.slf4j

The filter can be included in your configuration by adding a filter element in the undertow subsystem and referencing it through the host section:

<subsystem xmlns="urn:jboss:domain:undertow:3.1">
            <buffer-cache name="default"/>
            <server name="default-server">
                <http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true"/>
                <https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true"/>
                <host name="default-host" alias="localhost">
                    <location name="/" handler="welcome-content"/>
                    <filter-ref name="server-header"/>
                    <filter-ref name="x-powered-by-header"/>
                    <filter-ref name="myfilter"/>
                </host>
            </server>
            <servlet-container name="default">
                <jsp-config/>
                <websockets/>
            </servlet-container>
            <handlers>
                <file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
            </handlers>
            <filters>
                <response-header name="server-header" header-name="Server" header-value="WildFly/10"/>
                <response-header name="x-powered-by-header" header-name="X-Powered-By" header-value="Undertow/1"/>
                <filter name="myfilter" 
                        module="com.mastertheboss.handler" 
                        class-name="com.mastertheboss.MyHandler"/>
            </filters>
          
</subsystem>

Creating a Listener when the exchange is completed

The filter we have described so far runs in the pipeline of Handlers which are executed during the HTTP Request exchange. It is however possible to receive a Callback when the Exchange has completed, by adding a class which implements the ExchangeCompletionListener interface. In the following example, we will dump some information about the caller, at the end of the HttpServerExchange process:

package com.mastertheboss;

import io.undertow.server.ExchangeCompletionListener;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyHandler implements HttpHandler {

	private static final Logger log = LoggerFactory.getLogger(MyHandler.class);

	private HttpHandler next;
	private String param1;

	public MyHandler(HttpHandler next) {
		this.next = next;
	}

	@Override
	public void handleRequest(HttpServerExchange exchange) throws Exception {

		System.out.println("Received request from " + exchange.getDestinationAddress().getHostName());
		PostExchangeListener mylistener = new PostExchangeListener();

		exchange.addExchangeCompleteListener(mylistener);
		next.handleRequest(exchange);
	}

	private class PostExchangeListener implements ExchangeCompletionListener {

		@Override
		public void exchangeEvent(HttpServerExchange exchange, NextListener nextListener) {

			try {

   			   System.out.println("Reply from " + exchange.getSourceAddress().getHostName());
			} finally {

				if (nextListener != null) {
					nextListener.proceed();
				}
			}
		}

	}
}

Adding Parameters to your Filters

As final note, we will mention that it's possible to parametrize your Filters by including variables in the Handler as follows:

public class MyHandler implements HttpHandler {

	private static final Logger log = LoggerFactory.getLogger(MyHandler.class);

	private HttpHandler next;
	private String parameter1;

	public MyHandler(HttpHandler next) {
		this.next = next;
	}

	@Override
	public void handleRequest(HttpServerExchange exchange) throws Exception {

		. . . .
	}

	public void setParameter1(String s) {
		this.parameter1 = s;
	}
}

As you can see in the above code, the parameter "parameter1" has been added to the Handler class. The corresponding value will be coded into your configuration as a param element:

<filter name="myfilter" module="com.mastertheboss.handler" class-name="com.mastertheboss.MyHandler">
  <param name="parameter1" value="value">
</filter>

Follow us on Twitter