2

We have legacy code using Apache Http Client 4 that replaces a response entity from within a response interceptor:

public class GzipResponseInterceptor implements HttpResponseInterceptor
{
    @Override
    public void process(HttpResponse response, HttpContext context) throws HttpException, IOException
    {
        response.setEntity(new GzipDecompressingEntity(response.getEntity()));
    }
}

The interface HttpResponse for version 5 does not include a method for getting or setting the entity. The process method includes an EntityDetails parameter to get some information about the entity, but you cannot use that to replace it.

The Javadoc for the interceptor mentions decorating an entity, but does not mention how to do that:

Interceptors can also manipulate content entities enclosed with messages. Usually this is accomplished by using the 'Decorator' pattern where a wrapper entity class is used to decorate the original entity.

Does anyone know how this is meant to be done?

SPU42
  • 31
  • 3

2 Answers2

0

HttpResponse doesn't, but HttpEntityContainer does. Cast your response to it (after an instanceof check if you're not sure), then you can call setEntity again.

Rob Spoor
  • 6,186
  • 1
  • 19
  • 20
  • I was hoping for something more elegant with the interfaces provided, but it seems either this cast or a refactor to ExecChainHandler are the ways ahead. – SPU42 Apr 29 '23 at 17:38
0

I would strongly recommend to use a custom exec interceptor ExecChainHandler if one wants to modify the request or response messages. Message interceptors are expected to work with the message headers only

try (final CloseableHttpClient httpclient = HttpClients.custom()

        // Add a simple request ID to each outgoing request

        .addRequestInterceptorFirst(new HttpRequestInterceptor() {

            private final AtomicLong count = new AtomicLong(0);

            @Override
            public void process(
                    final HttpRequest request,
                    final EntityDetails entity,
                    final HttpContext context) throws HttpException, IOException {
                request.setHeader("request-id", Long.toString(count.incrementAndGet()));
            }
        })

        // Simulate a 404 response for some requests without passing the message down to the backend

        .addExecInterceptorAfter(ChainElement.PROTOCOL.name(), "custom", (request, scope, chain) -> {

            final Header idHeader = request.getFirstHeader("request-id");
            if (idHeader != null && "13".equalsIgnoreCase(idHeader.getValue())) {
                final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_NOT_FOUND, "Oppsie");
                response.setEntity(new StringEntity("bad luck", ContentType.TEXT_PLAIN));
                return response;
            } else {
                return chain.proceed(request, scope);
            }
        })
        .build()) {

    for (int i = 0; i < 20; i++) {
        final HttpGet httpget = new HttpGet("http://httpbin.org/get");

        System.out.println("Executing request " + httpget.getMethod() + " " + httpget.getUri());

        httpclient.execute(httpget, response -> {
            System.out.println("----------------------------------------");
            System.out.println(httpget + "->" + new StatusLine(response));
            EntityUtils.consume(response.getEntity());
            return null;
        });
    }
}

ok2c
  • 26,450
  • 5
  • 63
  • 71
  • I had seen an example like this, but it's a larger change that will affect users of this class. I was hoping the available interfaces provided something that I was just missing. – SPU42 Apr 29 '23 at 17:30
  • 1
    The message interceptors intentionally do not have a means of accessing the enclosed entity, so they can work with both the classic and the async pipelines exactly the same. It is strongly advised to use exec interceptors for message content manipulations – ok2c May 01 '23 at 07:25