Requested URI Discovery

Control how Helidon uses Forwarded and X-Forwarded-* headers on incoming requests and write application code to obtain consistent and trusted information from those headers.

Requested URI Discovery

Proxies and reverse proxies between an HTTP client and your Helidon application mask important information (for example Host header, originating IP address, protocol) about the request the client sent. Fortunately, many of these intermediary network nodes set or update either the standard HTTP Forwarded header or the non-standard X-Forwarded-* family of headers to preserve information about the original client request.

Helidon’s requested URI discovery feature allows your application—​and Helidon itself—​to reconstruct information about the original request using the Forwarded header and the X-Forwarded-* family of headers.

When you prepare the sockets in your server you can include the following optional requested URI discovery settings:

  • enabled or disabled

  • which type or types of requested URI discovery to use:

    • FORWARDED - uses the Forwarded header

    • X_FORWARDED - uses the X-Forwarded-* headers

    • HOST - uses the Host header

  • what intermediate nodes to trust

When your application invokes request.requestedUri() Helidon iterates through the discovery types you set up for the receiving socket, gathering information from the corresponding header(s) for that type. If the request does not have the corresponding header(s), or your settings do not trust the intermediate nodes reflected in those headers, then Helidon tries the next discovery type you set up. Helidon uses the HOST discovery type if you do not set up discovery yourself or if, for a particular request, it cannot assemble the request information using any discovery type you did set up for the socket.

Setting Up Requested URI Discovery Programmatically

To set up requested URI discovery on the default socket for your server, use the WebServer.Builder:

Requested URI set-up for the default server socket
import io.helidon.common.configurable.AllowList;
import static io.helidon.webserver.SocketConfiguration.RequestedUriDiscoveryType.FORWARDED;
import static io.helidon.webserver.SocketConfiguration.RequestedUriDiscoveryType.X_FORWARDED;

AllowList trustedProxies = AllowList.builder()
        .addAllowedPattern(Pattern.compile("lb.+\\.mycorp\\.com"))
        .addDenied("lbtest.mycorp.com")
        .build(); 

WebServer.Builder builder = WebServer.builder()
        .defaultSocket(s -> s
                .host("localhost")
                .port(0)
                .requestedUriDiscoveryTypes(List.of(FORWARDED, X_FORWARDED)) 
                .trustedProxies(trustedProxies)) 
        .addRouting(yourRouting)
        .config(serverConfig);
Copied
  • Create the AllowList describing the intermediate networks nodes to trust and not trust. Presumably the lbxxx.mycorp.com nodes are trusted load balancers except for the test load balancer lbtest, and no other nodes are trusted. AllowList accepts prefixes, suffixes, predicates, regex patterns, and exact matches. See the AllowList JavaDoc for complete information.
  • Use Forwarded first, then try X-Forwarded-* on each request.
  • Set the AllowList for trusted intermediaries.

If you build your server with additional sockets, you can control requested URI discovery separately for each.

Setting Up Requested URI Discovery using Configuration

You can also use configuration to set up the requested URI discovery behavior. The following example replicates the settings assigned programmatically in the earlier code example:

Configuring requested URI behavior
server:
  port: 0
  requested-uri-discovery:
    types: FORWARDED,X_FORWARDED
    trusted-proxies:
      allow:
        pattern: "lb.*\\.mycorp\\.com"
      deny:
        exact: "lbtest.mycorp.com""
Copied

Obtaining the Requested URI Information

Your code obtains the requested URI information from the Helidon server request object:

Retrieving Requested URI Information
import io.helidon.common.http.UriInfo;

public class MyHandler implements Handler {

    @Override
    public void accept(ServerRequest req, ServerResponse res) {
        UriInfo uriInfo = req.requestedUri();
        // ...
    }
}
Copied

See the UriInfo JavaDoc for more information.