private void handleChunk(
      HttpContent chunk, //
      final Channel channel, //
      final NettyResponseFuture<?> future, //
      AsyncHandler<?> handler)
      throws IOException, Exception {

    boolean interrupt = false;
    boolean last = chunk instanceof LastHttpContent;

    // Netty 4: the last chunk is not empty
    if (last) {
      LastHttpContent lastChunk = (LastHttpContent) chunk;
      HttpHeaders trailingHeaders = lastChunk.trailingHeaders();
      if (!trailingHeaders.isEmpty()) {
        interrupt =
            handler.onHeadersReceived(new HttpResponseHeaders(trailingHeaders, true))
                != State.CONTINUE;
      }
    }

    ByteBuf buf = chunk.content();
    if (!interrupt
        && !(handler instanceof StreamedAsyncHandler)
        && (buf.readableBytes() > 0 || last)) {
      HttpResponseBodyPart part =
          config.getResponseBodyPartFactory().newResponseBodyPart(buf, last);
      interrupt = updateBodyAndInterrupt(future, handler, part);
    }

    if (interrupt || last) finishUpdate(future, channel, !last);
  }
 private boolean updateBodyAndInterrupt(
     NettyResponseFuture<?> future, AsyncHandler<?> handler, HttpResponseBodyPart bodyPart)
     throws Exception {
   boolean interrupt = handler.onBodyPartReceived(bodyPart) != State.CONTINUE;
   if (interrupt) future.setKeepAlive(false);
   return interrupt;
 }
 private boolean handleHanderAndExit(
     Channel channel,
     NettyResponseFuture<?> future,
     AsyncHandler<?> handler,
     HttpResponseStatus status,
     HttpResponseHeaders responseHeaders,
     HttpResponse response)
     throws Exception {
   if (!future.getAndSetStatusReceived(true)
       && (handler.onStatusReceived(status) != STATE.CONTINUE
           || handler.onHeadersReceived(responseHeaders) != STATE.CONTINUE)) {
     finishUpdate(future, channel, HttpHeaders.isTransferEncodingChunked(response));
     return true;
   }
   return false;
 }
 private final boolean updateBodyAndInterrupt(
     NettyResponseFuture<?> future, AsyncHandler<?> handler, HttpResponseBodyPart bodyPart)
     throws Exception {
   boolean state = handler.onBodyPartReceived(bodyPart) != STATE.CONTINUE;
   if (bodyPart.isUnderlyingConnectionToBeClosed()) {
     future.setKeepAlive(false);
   }
   return state;
 }
 @Override
 public <T> ListenableFuture<T> execute(Request request, final AsyncHandler<T> asyncHandler) {
   try {
     return requestSender.sendRequest(request, asyncHandler, null, false);
   } catch (Exception e) {
     asyncHandler.onThrowable(e);
     return new ListenableFuture.CompletedFailure<>(e);
   }
 }
 private boolean exitAfterHandlingHeaders(
     Channel channel,
     NettyResponseFuture<?> future,
     HttpResponse response,
     AsyncHandler<?> handler,
     HttpResponseHeaders responseHeaders,
     HttpRequest httpRequest)
     throws IOException, Exception {
   return !response.headers().isEmpty()
       && handler.onHeadersReceived(responseHeaders) != State.CONTINUE;
 }
 private boolean exitAfterHandlingStatus(
     Channel channel,
     NettyResponseFuture<?> future,
     HttpResponse response,
     AsyncHandler<?> handler,
     NettyResponseStatus status,
     HttpRequest httpRequest)
     throws IOException, Exception {
   return !future.getAndSetStatusReceived(true)
       && handler.onStatusReceived(status) != State.CONTINUE;
 }
  @Override
  public void handle(final Channel channel, final NettyResponseFuture<?> future, final Object e)
      throws Exception {
    future.touch();

    // The connect timeout occurred.
    if (future.isCancelled() || future.isDone()) {
      channels.finishChannel(channel);
      return;
    }

    NettyRequest nettyRequest = future.getNettyRequest();
    AsyncHandler<?> handler = future.getAsyncHandler();
    ProxyServer proxyServer = future.getProxyServer();
    try {
      if (e instanceof HttpResponse) {
        HttpResponse response = (HttpResponse) e;
        LOGGER.debug("\n\nRequest {}\n\nResponse {}\n", nettyRequest.getHttpRequest(), response);
        future.setPendingResponse(response);
        return;
      }

      if (e instanceof HttpContent) {
        HttpResponse response = future.getPendingResponse();
        future.setPendingResponse(null);
        if (handler != null) {
          if (response != null
              && handleResponseAndExit(
                  channel, future, handler, nettyRequest.getHttpRequest(), proxyServer, response)) {
            return;
          }

          HttpContent chunk = (HttpContent) e;

          boolean interrupt = false;
          boolean last = chunk instanceof LastHttpContent;

          if (last) {
            LastHttpContent lastChunk = (LastHttpContent) chunk;
            HttpHeaders trailingHeaders = lastChunk.trailingHeaders();
            if (!trailingHeaders.isEmpty()) {
              interrupt =
                  handler.onHeadersReceived(
                          new ResponseHeaders(
                              future.getURI(), future.getHttpHeaders(), trailingHeaders))
                      != STATE.CONTINUE;
            }
          }

          ByteBuf buf = chunk.content();
          try {
            if (!interrupt && (buf.readableBytes() > 0 || last)) {
              interrupt =
                  updateBodyAndInterrupt(
                      future,
                      handler,
                      nettyConfig.getBodyPartFactory().newResponseBodyPart(buf, last));
            }
          } finally {
            // FIXME we shouldn't need this, should we? But a leak was reported there without it?!
            buf.release();
          }

          if (interrupt || last) {
            finishUpdate(future, channel, !last);
          }
        }
      }
    } catch (Exception t) {
      if (hasIOExceptionFilters
          && t instanceof IOException
          && requestSender.applyIoExceptionFiltersAndReplayRequest(
              future, IOException.class.cast(t), channel)) {
        return;
      }

      try {
        channels.abort(future, t);
      } finally {
        finishUpdate(future, channel, false);
        throw t;
      }
    }
  }