0

I'm using Apache's HttpAsyncClient to proxy, or relay, incoming requests to another server. I have the simple case working (sample code below).

The problem is that the Future<HttpResponse> will hold the response in memory, waiting until it's done reading before completing. We have some calls that have very large response bodies, so what I want to do is a "pipeline" or "zero copy" strategy, where I just pass along the body contents as they come, without holding the whole response in memory.

Apache Http Client provides a ZeroCopyPost and ZeroCopyConsumer, but they operate on Files rather than streams (i.e. the OutputStream of the response).

How can I write the code to pass the bytes from the remote response directly back to the original response?

My code so far:

    try {
        httpClient.start();

        Future<HttpResponse> future = httpClient.execute(method, null);
        HttpResponse remoteResponse = future.get();

        relayStatus(remoteResponse, originalResponse);
        relayHeaders(remoteResponse, originalResponse);
        relayBody(remoteResponse, originalResponse);
    }

    catch (IOException | InterruptedException |ExecutionException  e) {
        ...
    }

I've been having trouble finding direct example code for doing this, but the sample linked in comments takes me a lot farther. I think the answer will be along the lines of using AsyncByteConsumer

Am I on the right track? How can I extend that to use the zero copy strategy to pass the body bytes from the relayed response back to the original response?

sea-rob
  • 2,275
  • 1
  • 22
  • 22
  • 1
    Maybe I'm missing the gist of the question, but you could do it streaming as shown [in an example](https://hc.apache.org/httpcomponents-asyncclient-4.0.x/httpasyncclient/examples/org/apache/http/examples/nio/client/AsyncClientHttpExchangeStreaming.java) or try out the beta-version of the [HttpPipeliningClient](https://hc.apache.org/httpcomponents-asyncclient-4.1.x/httpasyncclient/apidocs/org/apache/http/nio/client/HttpPipeliningClient.html) – vanOekel Jan 15 '15 at 23:43

1 Answers1

1

So vanOekel's comment gave me the hint I needed; based on the example he linked, I created a "PipelineStreamConsumer" and then used that in the httpClient.execute(...) call.

I'm posting this for posterity, and in case someone wants to score some points with a better answer.

Future<HttpResponse> future = httpClient.execute(producer, new PipelineStreamConsumer(originalResponse), null);

...

public class PipelineStreamConsumer extends AsyncByteConsumer<HttpResponse> {

    private final HttpServletResponse response;
    private HttpResponse remoteResponse;

    public PipelineStreamConsumer(HttpServletResponse response) {
        this.response = response;
    }

    @Override
    protected void onResponseReceived(HttpResponse remoteResponse) throws HttpException,
    IOException {
        this.remoteResponse = remoteResponse;
        relayStatus(remoteResponse);
        relayHeaders(remoteResponse);
    }

    @Override
    protected HttpResponse buildResult(HttpContext context) throws Exception {
        return remoteResponse;
    }

    @Override
    protected void onByteReceived(ByteBuffer buffer, IOControl control)
            throws IOException {
        WritableByteChannel channel = Channels.newChannel(outputStream);
        channel.write(buffer);
    }
}
sea-rob
  • 2,275
  • 1
  • 22
  • 22