apache-httpcomponentsapache-httpclient-5.x

Apache HttpClient5 - How to set LocalAddress per-request?


I'm migrating Apache httpclient from 4.5 to 5.3 and my app currently chooses from a set of local IPs dynamically.

Example code:

HttpGet request = new HttpGet(url);
request.setRequestConfig(RequestConfig.custom()
    .setLocalAddress(someLocalInetAddress)
    .build());

In httpclient 5.x the setLocalAddress() has been removed and I need a new way to assign the local address.

I know I can create ConnectionSocketFactory on the ConnectionManager level but this doesn't work for me because it would require explicit configuration for when to use each IP (as far as I understand). I have some other logic which determines the IP and I simply need to set it for the request.

UPDATE:

A "hack" I've found is to create a custom HttpContext and pass it like this

//create a new HttpRoute route and set its local address
final HttpContext context = HttpClientContext.create();
context.setAttribute(HTTP_ROUTE, httpRoute);

client.execute(request, context, handler);

The issue with this is that it overrides the whole httpRoute, not just the localAddress.


Solution

  • There is no such thing as a local address per request. It makes no sense. The local address can only be defined at the HTTP route level.

    Subclass DefaultRoutePlanner and override its #determineLocalAddress method in order to provide a custom strategy for a local address resolution on a per HTTP route basis.

    try (CloseableHttpClient httpclient = HttpClients.custom()
            .setRoutePlanner(new DefaultRoutePlanner(DefaultSchemePortResolver.INSTANCE) {
    
                @Override
                protected InetAddress determineLocalAddress(final HttpHost firstHop, final HttpContext context) throws HttpException {
                    return (InetAddress) context.getAttribute("local-address");
                }
    
            })
            .build()) {
    
        final ClassicHttpRequest request = ClassicRequestBuilder.get()
                .setHttpHost(new HttpHost("https", "httpbin.org"))
                .setPath("/headers")
                .build();
        final HttpClientContext clientContext = HttpClientContext.create();
        clientContext.setAttribute("local-address", InetAddress.getByAddress(new byte[] { 127, 0, 0, 1}));
        System.out.println("Executing request " + request);
        httpclient.execute(request, clientContext, response -> {
            System.out.println("----------------------------------------");
            System.out.println(request + "->" + new StatusLine(response));
            EntityUtils.consume(response.getEntity());
            return null;
        });
    }