Java Code Examples for io.netty.handler.codec.http.HttpContent#release()

The following examples show how to use io.netty.handler.codec.http.HttpContent#release() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: ZuulMessageImpl.java    From zuul with Apache License 2.0 6 votes vote down vote up
@Override
public void runBufferedBodyContentThroughFilter(ZuulFilter<?, ?> filter) {
    //Loop optimized for the common case: Most filters' processContentChunk() return
    // original chunk passed in as is without any processing
    for (int i=0; i < bodyChunks.size(); i++) {
        final HttpContent origChunk = bodyChunks.get(i);
        final HttpContent filteredChunk = filter.processContentChunk(this, origChunk);
        if ((filteredChunk != null) && (filteredChunk != origChunk)) {
            //filter actually did some processing, set the new chunk in and release the old chunk.
            bodyChunks.set(i, filteredChunk);
            final int refCnt = origChunk.refCnt();
            if (refCnt > 0) {
                origChunk.release(refCnt);
            }
        }
    }
}
 
Example 2
Source File: HttpPostRequestEncoderTest.java    From netty-4.1.22 with Apache License 2.0 6 votes vote down vote up
private static void checkNextChunkSize(HttpPostRequestEncoder encoder, int sizeWithoutDelimiter) throws Exception {
    // 16 bytes as HttpPostRequestEncoder uses Long.toHexString(...) to generate a hex-string which will be between
    // 2 and 16 bytes.
    // See https://github.com/netty/netty/blob/4.1/codec-http/src/main/java/io/netty/handler/
    // codec/http/multipart/HttpPostRequestEncoder.java#L291
    int expectedSizeMin = sizeWithoutDelimiter + 2;
    int expectedSizeMax = sizeWithoutDelimiter + 16;

    HttpContent httpContent = encoder.readChunk((ByteBufAllocator) null);

    int readable = httpContent.content().readableBytes();
    boolean expectedSize = readable >= expectedSizeMin && readable <= expectedSizeMax;
    assertTrue("Chunk size is not in expected range (" + expectedSizeMin + " - " + expectedSizeMax + "), was: "
            + readable, expectedSize);
    httpContent.release();
}
 
Example 3
Source File: Http2StreamFrameToHttpObjectCodecTest.java    From netty-4.1.22 with Apache License 2.0 6 votes vote down vote up
@Test
public void testDowngradeData() throws Exception {
    EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true));
    ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
    assertTrue(ch.writeInbound(new DefaultHttp2DataFrame(hello)));

    HttpContent content = ch.readInbound();
    try {
        assertThat(content.content().toString(CharsetUtil.UTF_8), is("hello world"));
        assertFalse(content instanceof LastHttpContent);
    } finally {
        content.release();
    }

    assertThat(ch.readInbound(), is(nullValue()));
    assertFalse(ch.finish());
}
 
Example 4
Source File: Http2StreamFrameToHttpObjectCodecTest.java    From netty-4.1.22 with Apache License 2.0 6 votes vote down vote up
@Test
public void testDecodeDataAsClient() throws Exception {
    EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false));
    ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
    assertTrue(ch.writeInbound(new DefaultHttp2DataFrame(hello)));

    HttpContent content = ch.readInbound();
    try {
        assertThat(content.content().toString(CharsetUtil.UTF_8), is("hello world"));
        assertFalse(content instanceof LastHttpContent);
    } finally {
        content.release();
    }

    assertThat(ch.readInbound(), is(nullValue()));
    assertFalse(ch.finish());
}
 
Example 5
Source File: NettyResponseChannelTest.java    From ambry with Apache License 2.0 6 votes vote down vote up
/**
 * Send the curated request and verify that the response and satisfaction are expected.
 * @param httpRequest the request to be handled by {@link MockNettyMessageProcessor}
 * @param requestContent the content needed for POST request
 * @param expectedStatus the expected {@link HttpResponseStatus}
 * @param shouldBeSatisfied whether the request should be satisfied or not
 * @throws IOException
 */
private void sendRequestAndEvaluateResponsePerformance(HttpRequest httpRequest, String requestContent,
    HttpResponseStatus expectedStatus, boolean shouldBeSatisfied) throws IOException {
  EmbeddedChannel channel = createEmbeddedChannel();
  channel.pipeline().get(MockNettyMessageProcessor.class);
  channel.writeInbound(httpRequest);
  if (requestContent != null) {
    channel.writeInbound(createContent(requestContent, true));
  }
  HttpResponse response = channel.readOutbound();
  assertEquals("Unexpected response status", expectedStatus, response.status());
  if (requestContent != null) {
    HttpContent responseContent = channel.readOutbound();
    String returnedContent = RestTestUtils.getContentString(responseContent);
    responseContent.release();
    assertEquals("Content does not match with expected content", requestContent, returnedContent);
  }
  if (shouldBeSatisfied) {
    assertTrue(httpRequest.method() + " request should be satisfied", MockNettyRequest.mockTracker.isSatisfied());
  } else {
    assertFalse(httpRequest.method() + " request should be unsatisfied", MockNettyRequest.mockTracker.isSatisfied());
  }
}
 
Example 6
Source File: FrontendIntegrationTest.java    From ambry with Apache License 2.0 6 votes vote down vote up
/**
 * Combines all the parts in {@code contents} into one {@link ByteBuffer}.
 * @param contents the content of the response.
 * @param expectedContentLength the length of the contents in bytes.
 * @return a {@link ByteBuffer} that contains all the data in {@code contents}.
 */
private ByteBuffer getContent(Queue<HttpObject> contents, long expectedContentLength) {
  ByteBuffer buffer = ByteBuffer.allocate((int) expectedContentLength);
  boolean endMarkerFound = false;
  for (HttpObject object : contents) {
    assertFalse("There should have been no more data after the end marker was found", endMarkerFound);
    HttpContent content = (HttpContent) object;
    buffer.put(content.content().nioBuffer());
    endMarkerFound = object instanceof LastHttpContent;
    content.release();
  }
  assertEquals("Content length did not match expected", expectedContentLength, buffer.position());
  assertTrue("End marker was not found", endMarkerFound);
  buffer.flip();
  return buffer;
}
 
Example 7
Source File: ProxyRouterEndpointExecutionHandler.java    From riposte with Apache License 2.0 5 votes vote down vote up
/**
 * @return true if the stream has failed and the given content chunk released, false if the stream has *not*
 * yet failed.
 */
protected boolean releaseContentChunkIfStreamAlreadyFailed(
    HttpContent content, ProxyRouterProcessingState proxyRouterState
) {
    if (proxyRouterState.isRequestStreamingCancelled()) {
        // A previous chunk failed to stream, and we marked the proxyRouterState as having failed.
        //      The previous chunk would have already caused the failure response to be sent to the
        //      caller, therefore we don't need to do anything else for *this* chunk except release it.
        content.release();
        return true;
    }

    return false;
}
 
Example 8
Source File: OriginResponseReceiver.java    From zuul with Apache License 2.0 5 votes vote down vote up
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
    if (msg instanceof HttpResponse) {
        if (edgeProxy != null) {
            edgeProxy.responseFromOrigin((HttpResponse) msg);
        }
        ctx.channel().read();
    }
    else if (msg instanceof HttpContent) {
        final HttpContent chunk = (HttpContent) msg;
        if (edgeProxy != null) {
            edgeProxy.invokeNext(chunk);
        }
        else {
            chunk.release();
        }
        ctx.channel().read();
    }
    else {
        //should never happen
        ReferenceCountUtil.release(msg);
        final Exception error = new IllegalStateException("Received invalid message from origin");
        if (edgeProxy != null) {
            edgeProxy.errorFromOrigin(error);
        }
        ctx.fireExceptionCaught(error);
    }
}
 
Example 9
Source File: HttpPostRequestEncoderTest.java    From netty4.0.27Learn with Apache License 2.0 5 votes vote down vote up
@Test
public void testHttpPostRequestEncoderSlicedBuffer() throws Exception {
    DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1,
            HttpMethod.POST, "http://localhost");

    HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(request, true);
    // add Form attribute
    encoder.addBodyAttribute("getform", "POST");
    encoder.addBodyAttribute("info", "first value");
    encoder.addBodyAttribute("secondinfo", "secondvalue a&");
    encoder.addBodyAttribute("thirdinfo", "short text");
    int length = 100000;
    char[] array = new char[length];
    Arrays.fill(array, 'a');
    String longText = new String(array);
    encoder.addBodyAttribute("fourthinfo", longText.substring(0, 7470));
    File file1 = new File(getClass().getResource("/file-01.txt").toURI());
    encoder.addBodyFileUpload("myfile", file1, "application/x-zip-compressed", false);
    encoder.finalizeRequest();
    while (! encoder.isEndOfInput()) {
        HttpContent httpContent = encoder.readChunk(null);
        if (httpContent.content() instanceof SlicedByteBuf) {
            assertEquals(2, httpContent.content().refCnt());
        } else {
            assertEquals(1, httpContent.content().refCnt());
        }
        httpContent.release();
    }
    encoder.cleanFiles();
    encoder.close();
}
 
Example 10
Source File: ZuulEndPointRunner.java    From zuul with Apache License 2.0 5 votes vote down vote up
@Override
public void filter(final HttpRequestMessage zuulReq, final HttpContent chunk) {
    if (zuulReq.getContext().isCancelled()) {
        chunk.release();
        return;
    }

    String endpointName = "-";
    PerfMark.startTask(getClass().getName(), "filterChunk");
    try {
        addPerfMarkTags(zuulReq);
        ZuulFilter<HttpRequestMessage, HttpResponseMessage> endpoint = Preconditions.checkNotNull(
                getEndpoint(zuulReq), "endpoint");
        endpointName = endpoint.filterName();

        final HttpContent newChunk = endpoint.processContentChunk(zuulReq, chunk);
        if (newChunk != null) {
            //Endpoints do not directly forward content chunks to next stage in the filter chain.
            zuulReq.bufferBodyContents(newChunk);

            //deallocate original chunk if necessary
            if (newChunk != chunk) {
                chunk.release();
            }

            if (isFilterAwaitingBody(zuulReq) && zuulReq.hasCompleteBody() && !(endpoint instanceof ProxyEndpoint)) {
                //whole body has arrived, resume filter chain
                invokeNextStage(filter(endpoint, zuulReq));
            }
        }
    }
    catch (Exception ex) {
        handleException(zuulReq, endpointName, ex);
    } finally {
        PerfMark.stopTask(getClass().getName(), "filterChunk");
    }
}
 
Example 11
Source File: HttpRequestInputStream.java    From spring-boot-starter-netty with GNU General Public License v3.0 5 votes vote down vote up
/**
 * 关闭HttpContent队列
 */
private void closeHttpContentQueue() {
    while(!queue.isEmpty()){
        HttpContent content = queue.poll();
        if(content != null){
            content.release();
        }
    }
    queue.clear();
}
 
Example 12
Source File: Gzipper.java    From zuul with Apache License 2.0 5 votes vote down vote up
public void write(final HttpContent chunk) {
    try {
        write(chunk.content());
        gzos.flush();
    }
    catch(IOException ioEx) {
        throw new ZuulException(ioEx, "Error Gzipping response content chunk", true);
    }
    finally {
        chunk.release();
    }
}
 
Example 13
Source File: HttpDownSniffIntercept.java    From proxyee-down with Apache License 2.0 5 votes vote down vote up
@Override
public void afterResponse(Channel clientChannel, Channel proxyChannel, HttpContent httpContent,
    HttpProxyInterceptPipeline pipeline) throws Exception {
  if (downFlag) {
    httpContent.release();
  } else {
    pipeline.afterResponse(clientChannel, proxyChannel, httpContent);
  }
}
 
Example 14
Source File: HttpAuthUpstreamHandler.java    From crate with Apache License 2.0 5 votes vote down vote up
private void handleHttpChunk(ChannelHandlerContext ctx, HttpContent msg) {
    if (authorizedUser == null) {
        // We won't forward the message downstream, thus we have to release
        msg.release();
        sendUnauthorized(ctx.channel(), null);
    } else {
        ctx.fireChannelRead(msg);
    }
}
 
Example 15
Source File: StreamingAsyncHttpClient.java    From riposte with Apache License 2.0 4 votes vote down vote up
protected ChannelFuture doStreamChunk(HttpContent chunkToWrite) {
    // We are in the channel's event loop. Do some final checks to make sure it's still ok to write and flush
    //      the message, then do it (or handle the special cases appropriately).
    try {
        boolean chunkIsLastHttpContent = (chunkToWrite instanceof LastHttpContent);

        if (
            downstreamLastChunkSentHolder.heldObject
            && chunkIsLastHttpContent
            && chunkToWrite.content().readableBytes() == 0
        ) {
            // A LastHttpContent has already been written downstream. This is valid/legal when the downstream call
            //      has a content-length header, and the downstream pipeline encoder realizes there's no more
            //      content to write and generates a synthetic empty LastHttpContent rather than waiting for this
            //      one to arrive. Therefore there's nothing to do but handle the wire-send-finish annotation
            //      (if not already done), release the content chunk, and return an
            //      already-successfully-completed future.
            if (logger.isDebugEnabled()) {
                runnableWithTracingAndMdc(
                    () -> logger.warn("Ignoring zero-length LastHttpContent from upstream, because a "
                                      + "LastHttpContent has already been sent downstream."),
                    distributedTracingSpanStack, distributedTracingMdcInfo
                ).run();
            }
            handleWireSendFinishAnnotationIfNecessary();
            chunkToWrite.release();
            return channel.newSucceededFuture();
        }

        if (!callActiveHolder.heldObject) {
            chunkToWrite.release();
            return channel.newFailedFuture(
                new RuntimeException("Unable to stream chunk - downstream call is no longer active.")
            );
        }

        if (channelClosedDueToUnrecoverableError) {
            chunkToWrite.release();
            return channel.newFailedFuture(
                new RuntimeException("Unable to stream chunks downstream - the channel was closed previously "
                                     + "due to an unrecoverable error")
            );
        }

        // Handle the wire-send-finish annotation if this is the last chunk.
        if (chunkIsLastHttpContent) {
            handleWireSendFinishAnnotationIfNecessary();
        }

        return channel.writeAndFlush(chunkToWrite);
    }
    catch(Throwable t) {
        String errorMsg =
            "StreamingChannel.doStreamChunk() threw an exception. This should not be possible and indicates "
            + "something went wrong with a Netty write() and flush(). If you see Netty memory leak warnings "
            + "then this could be why. Please report this along with the stack trace to "
            + "https://github.com/Nike-Inc/riposte/issues/new";

        Exception exceptionToPass = new RuntimeException(errorMsg, t);

        logger.error(errorMsg, exceptionToPass);
        return channel.newFailedFuture(exceptionToPass);
    }
}
 
Example 16
Source File: ProxyRouterEndpointExecutionHandler.java    From riposte with Apache License 2.0 4 votes vote down vote up
protected void sendContentChunkDownPipeline(HttpContent contentChunk,
                                            OutboundMessageSendContentChunk contentChunkToSend,
                                            HttpProcessingState state) {
    boolean stateResponseSendingLastChunkSent = state != null && state.isResponseSendingLastChunkSent();
    if (lastChunkSent || stateResponseSendingLastChunkSent || cancelStreamingToOriginalCaller) {
        // A full response has already been sent to the user, so there's no point in firing this content chunk
        //      down the pipeline. The response was likely an error response due to an error that occurred
        //      before any of the downstream proxied response was received. We'll log a message here for
        //      debugging purposes, but otherwise ignore this content chunk.
        runnableWithTracingAndMdc(
            () -> logger.warn(
                "Ignoring response content chunk from the downstream call because a full response was already "
                + "sent to the user (likely an error response)."
            ),
            ctx
        ).run();
        // Since this chunk won't be written and flushed down the channel we have to release the message
        //      here to avoid a memory leak.
        contentChunk.release();

        Throwable requestAlreadyHandledException = new RuntimeException(
            "A response has already been sent to the original caller (likely an error response) - "
            + "ignoring proxied response content chunk."
        );
        // Stop streaming request chunks downstream.
        proxyRouterProcessingState.cancelRequestStreaming(requestAlreadyHandledException, ctx);

        // And since we know we will never be able to use anything from the downstream call,
        //      cancel response streaming from the downstream call as well.
        proxyRouterProcessingState.cancelDownstreamRequest(requestAlreadyHandledException);
    }
    else {
        // We haven't already sent a last chunk to the user, which means no error response has occurred.
        //      Therefore the in-progress response is still the downstream call's response, and we are free to
        //      send this response chunk from the downstream call back to the original caller.
        if (contentChunk instanceof LastHttpContent) 
            lastChunkSent = true;

        ctx.fireChannelRead(contentChunkToSend);
    }
}
 
Example 17
Source File: MSF4JHttpConnectorListener.java    From msf4j with Apache License 2.0 4 votes vote down vote up
/**
 * Dispatch appropriate resource method.
 */
private void dispatchMethod(MicroservicesRegistryImpl registry, Request request, Response response)
        throws Exception {
    HttpUtil.setConnectionHeader(request, response);
    PatternPathRouter.RoutableDestination<HttpResourceModel> destination = registry.getMetadata()
            .getDestinationMethod(
                    request.getUri(),
                    request.getHttpMethod(),
                    request.getContentType(),
                    request.getAcceptTypes());
    HttpResourceModel resourceModel = destination.getDestination();
    response.setMediaType(Util.getResponseType(request.getAcceptTypes(), resourceModel.getProducesMediaTypes()));
    HttpMethodInfoBuilder httpMethodInfoBuilder =
            new HttpMethodInfoBuilder().httpResourceModel(resourceModel).httpRequest(request)
                    .httpResponder(response).requestInfo(destination.getGroupNameValues());
    HttpMethodInfo httpMethodInfo = httpMethodInfoBuilder.build();
    if (httpMethodInfo.isStreamingSupported()) {
        Method method = resourceModel.getMethod();
        Class<?> clazz = method.getDeclaringClass();
        // Execute request interceptors
        if (InterceptorExecutor.executeGlobalRequestInterceptors(registry, request, response)
                // Execute class level request interceptors
                && InterceptorExecutor.executeClassLevelRequestInterceptors(request, response, clazz)
                // Execute method level request interceptors
                && InterceptorExecutor.executeMethodLevelRequestInterceptors(request, response, method)) {

            HttpCarbonMessage carbonMessage = getHttpCarbonMessage(request);
            HttpContent httpContent = carbonMessage.getHttpContent();
            while (true) {
                if (httpContent == null) {
                    break;
                }
                httpMethodInfo.chunk(httpContent.content().nioBuffer());
                httpContent.release();
                // Exit the loop at the end of the content
                if (httpContent instanceof LastHttpContent) {
                    break;
                }
                httpContent = carbonMessage.getHttpContent();
            }
            boolean isResponseInterceptorsSuccessful =
                    InterceptorExecutor.executeMethodLevelResponseInterceptors(request, response, method)
                            // Execute class level interceptors (first in - last out order)
                            && InterceptorExecutor.executeClassLevelResponseInterceptors(request, response,
                            clazz)
                            // Execute global interceptors
                            && InterceptorExecutor.executeGlobalResponseInterceptors(registry, request,
                            response);
            httpMethodInfo.end(isResponseInterceptorsSuccessful);
        }
    } else {
        httpMethodInfo.invoke(destination, request, httpMethodInfo, registry);
    }
}
 
Example 18
Source File: HttpRequest.java    From blade with Apache License 2.0 4 votes vote down vote up
public void init(String remoteAddress) {
    this.remoteAddress = remoteAddress.substring(1);
    this.keepAlive = HttpUtil.isKeepAlive(nettyRequest);
    this.url = nettyRequest.uri();

    int pathEndPos = this.url().indexOf('?');
    this.uri = pathEndPos < 0 ? this.url() : this.url().substring(0, pathEndPos);
    this.protocol = nettyRequest.protocolVersion().text();
    this.method = nettyRequest.method().name();

    String cleanUri = this.uri;
    if (!"/".equals(this.contextPath())) {
        cleanUri = PathKit.cleanPath(cleanUri.replaceFirst(this.contextPath(), "/"));
        this.uri = cleanUri;
    }

    this.httpHeaders = nettyRequest.headers();

    if (!HttpServerHandler.PERFORMANCE) {
        SessionManager sessionManager = WebContext.blade().sessionManager();
        if (null != sessionManager) {
            this.session = SESSION_HANDLER.createSession(this);
        }
    }

    if ("GET".equals(this.method())) {
        return;
    }

    try {
        HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(HTTP_DATA_FACTORY, nettyRequest);
        this.isMultipart = decoder.isMultipart();

        List<ByteBuf> byteBuffs = new ArrayList<>(this.contents.size());

        for (HttpContent content : this.contents) {
            if (!isMultipart) {
                byteBuffs.add(content.content().copy());
            }

            decoder.offer(content);
            this.readHttpDataChunkByChunk(decoder);
            content.release();
        }
        if (!byteBuffs.isEmpty()) {
            this.body = Unpooled.copiedBuffer(byteBuffs.toArray(new ByteBuf[0]));
        }
    } catch (Exception e) {
        throw new HttpParseException("build decoder fail", e);
    }
}
 
Example 19
Source File: NettyResponseChannelTest.java    From ambry with Apache License 2.0 4 votes vote down vote up
/**
 * Tests the common workflow of the {@link NettyResponseChannel} i.e., add some content to response body via
 * {@link NettyResponseChannel#write(ByteBuffer, Callback)} and then complete the response.
 * <p/>
 * These responses have the header Content-Length set.
 * @throws Exception
 */
@Test
public void responsesWithContentLengthTest() throws Exception {
  EmbeddedChannel channel = createEmbeddedChannel();
  MockNettyMessageProcessor processor = channel.pipeline().get(MockNettyMessageProcessor.class);
  final int ITERATIONS = 10;
  for (int i = 0; i < ITERATIONS; i++) {
    boolean isKeepAlive = i != (ITERATIONS - 1);
    HttpHeaders httpHeaders = new DefaultHttpHeaders();
    httpHeaders.set(MockNettyMessageProcessor.CHUNK_COUNT_HEADER_NAME, i);
    HttpRequest httpRequest =
        RestTestUtils.createRequest(HttpMethod.POST, TestingUri.ResponseWithContentLength.toString(), httpHeaders);
    HttpUtil.setKeepAlive(httpRequest, isKeepAlive);
    channel.writeInbound(httpRequest);
    verifyCallbacks(processor);

    // first outbound has to be response.
    HttpResponse response = channel.readOutbound();
    assertEquals("Unexpected response status", HttpResponseStatus.OK, response.status());
    long contentLength = HttpUtil.getContentLength(response, -1);
    assertEquals("Unexpected Content-Length", MockNettyMessageProcessor.CHUNK.length * i, contentLength);
    if (contentLength == 0) {
      // special case. Since Content-Length is set, the response should be an instance of FullHttpResponse.
      assertTrue("Response not instance of FullHttpResponse", response instanceof FullHttpResponse);
    } else {
      HttpContent httpContent = null;
      for (int j = 0; j < i; j++) {
        httpContent = channel.readOutbound();
        byte[] returnedContent = new byte[httpContent.content().readableBytes()];
        httpContent.content().readBytes(returnedContent);
        httpContent.release();
        assertArrayEquals("Content does not match with expected content", MockNettyMessageProcessor.CHUNK,
            returnedContent);
      }
      // When we know the content-length, the last httpContent would be an instance of LastHttpContent and there is no
      // empty last http content following it.
      // the last HttpContent should also be an instance of LastHttpContent
      assertTrue("The last part of the content is not LastHttpContent", httpContent instanceof LastHttpContent);
    }
    assertEquals("Unexpected channel state on the server", isKeepAlive, channel.isActive());
  }
}
 
Example 20
Source File: ClientResponseWriter.java    From zuul with Apache License 2.0 4 votes vote down vote up
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
    final Channel channel = ctx.channel();

    if (msg instanceof HttpResponseMessage) {
        final HttpResponseMessage resp = (HttpResponseMessage) msg;

        if (skipProcessing(resp)) {
            return;
        }

        if ((! isHandlingRequest) || (startedSendingResponseToClient)) {
            /* This can happen if we are already in the process of streaming response back to client OR NOT within active
               request/response cycle and something like IDLE or Request Read timeout occurs. In that case we have no way
               to recover other than closing the socket and cleaning up resources used by BOTH responses.
             */
            resp.disposeBufferedBody();
            if (zuulResponse != null) zuulResponse.disposeBufferedBody();
            ctx.close(); //This will trigger CompleteEvent if one is needed
            return;
        }

        startedSendingResponseToClient = true;
        zuulResponse = resp;
        if ("close".equalsIgnoreCase(zuulResponse.getHeaders().getFirst("Connection"))) {
            closeConnection = true;
        }
        channel.attr(ATTR_ZUUL_RESP).set(zuulResponse);

        if (channel.isActive()) {

            // Track if this is happening.
            if (! ClientRequestReceiver.isLastContentReceivedForChannel(channel)) {

                StatusCategory status = StatusCategoryUtils.getStatusCategory(ClientRequestReceiver.getRequestFromChannel(channel));
                if (ZuulStatusCategory.FAILURE_CLIENT_TIMEOUT.equals(status)) {
                    // If the request timed-out while being read, then there won't have been any LastContent, but thats ok because the connection will have to be discarded anyway.
                }
                else {
                    responseBeforeReceivedLastContentCounter.increment();
                    LOG.warn("Writing response to client channel before have received the LastContent of request! "
                            + zuulResponse.getInboundRequest().getInfoForLogging() + ", "
                            + ChannelUtils.channelInfoForLogging(channel));
                }
            }

            // Write out and flush the response to the client channel.
            channel.write(buildHttpResponse(zuulResponse));
            writeBufferedBodyContent(zuulResponse, channel);
            channel.flush();
        } else {
            channel.close();
        }
    }
    else if (msg instanceof HttpContent) {
        final HttpContent chunk = (HttpContent) msg;
        if (channel.isActive()) {
            channel.writeAndFlush(chunk);
        } else {
            chunk.release();
            channel.close();
        }
    }
    else {
        //should never happen
        ReferenceCountUtil.release(msg);
        throw new ZuulException("Received invalid message from origin", true);
    }
}