Search in sources :

Example 51 with HttpResponse

use of io.netty.handler.codec.http.HttpResponse in project riposte by Nike-Inc.

the class StreamingAsyncHttpClient method prepChannelForDownstreamCall.

protected void prepChannelForDownstreamCall(ChannelPool pool, Channel ch, StreamingCallback callback, Deque<Span> distributedSpanStackToUse, Map<String, String> mdcContextToUse, boolean isSecureHttpsCall, boolean relaxedHttpsValidation, boolean performSubSpanAroundDownstreamCalls, long downstreamCallTimeoutMillis, ObjectHolder<Boolean> callActiveHolder, ObjectHolder<Boolean> lastChunkSentDownstreamHolder) throws SSLException, NoSuchAlgorithmException, KeyStoreException {
    ChannelHandler chunkSenderHandler = new SimpleChannelInboundHandler<HttpObject>() {

        @Override
        protected void channelRead0(ChannelHandlerContext downstreamCallCtx, HttpObject msg) throws Exception {
            try {
                //      the call is fully processed should not trigger the behavior a second time.
                if (callActiveHolder.heldObject) {
                    if (msg instanceof LastHttpContent) {
                        lastChunkSentDownstreamHolder.heldObject = true;
                        if (performSubSpanAroundDownstreamCalls) {
                            // Complete the subspan.
                            runnableWithTracingAndMdc(() -> {
                                if (distributedSpanStackToUse == null || distributedSpanStackToUse.size() < 2)
                                    Tracer.getInstance().completeRequestSpan();
                                else
                                    Tracer.getInstance().completeSubSpan();
                            }, distributedSpanStackToUse, mdcContextToUse).run();
                        }
                    }
                    HttpObject msgToPass = msg;
                    if (msg instanceof HttpResponse) {
                        // We can't pass the original HttpResponse back to the callback due to intricacies of how
                        //      Netty handles determining the last chunk. If we do, and the callback ends up writing
                        //      the message out to the client (which happens during proxy routing for example), then
                        //      msg's headers might get modified - potentially causing this channel pipeline to
                        //      never send a LastHttpContent, which will in turn cause an indefinite hang.
                        HttpResponse origHttpResponse = (HttpResponse) msg;
                        HttpResponse httpResponse = (msg instanceof FullHttpResponse) ? new DefaultFullHttpResponse(origHttpResponse.getProtocolVersion(), origHttpResponse.getStatus(), ((FullHttpResponse) msg).content()) : new DefaultHttpResponse(origHttpResponse.getProtocolVersion(), origHttpResponse.getStatus());
                        httpResponse.headers().add(origHttpResponse.headers());
                        msgToPass = httpResponse;
                    }
                    callback.messageReceived(msgToPass);
                } else {
                    if (shouldLogBadMessagesAfterRequestFinishes) {
                        runnableWithTracingAndMdc(() -> logger.warn("Received HttpObject msg when call was not active: {}", String.valueOf(msg)), distributedSpanStackToUse, mdcContextToUse).run();
                    }
                }
            } finally {
                if (msg instanceof LastHttpContent) {
                    releaseChannelBackToPoolIfCallIsActive(ch, pool, callActiveHolder, "last content chunk sent", distributedSpanStackToUse, mdcContextToUse);
                }
            }
        }
    };
    Consumer<Throwable> doErrorHandlingConsumer = (cause) -> {
        Pair<Deque<Span>, Map<String, String>> originalThreadInfo = null;
        try {
            // Setup tracing and MDC so our log messages have the correct distributed trace info, etc.
            originalThreadInfo = linkTracingAndMdcToCurrentThread(distributedSpanStackToUse, mdcContextToUse);
            //      call is fully processed should not trigger the behavior a second time.
            if (callActiveHolder.heldObject) {
                if (performSubSpanAroundDownstreamCalls) {
                    if (distributedSpanStackToUse == null || distributedSpanStackToUse.size() < 2)
                        Tracer.getInstance().completeRequestSpan();
                    else
                        Tracer.getInstance().completeSubSpan();
                }
                Tracer.getInstance().unregisterFromThread();
                if (cause instanceof Errors.NativeIoException) {
                    // NativeIoExceptions are often setup to not have stack traces which is bad for debugging.
                    //      Wrap it in a NativeIoExceptionWrapper that maps to a 503 since this is likely a busted
                    //      connection and a second attempt should work.
                    cause = new NativeIoExceptionWrapper("Caught a NativeIoException in the downstream streaming call pipeline. Wrapped it in a " + "NativeIoExceptionWrapper so that it maps to a 503 and provides a usable stack trace " + "in the logs.", (Errors.NativeIoException) cause);
                }
                callback.unrecoverableErrorOccurred(cause);
            } else {
                if (cause instanceof DownstreamIdleChannelTimeoutException) {
                    logger.debug("A channel used for downstream calls will be closed because it was idle too long. " + "This is normal behavior and does not indicate a downstream call failure: {}", cause.toString());
                } else {
                    logger.warn("Received exception in downstream call pipeline after the call was finished. " + "Not necessarily anything to worry about but in case it helps debugging the " + "exception was: {}", cause.toString());
                }
            }
        } finally {
            // Mark the channel as broken so it will be closed and removed from the pool when it is returned.
            markChannelAsBroken(ch);
            // Release it back to the pool if possible/necessary so the pool can do its usual cleanup.
            releaseChannelBackToPoolIfCallIsActive(ch, pool, callActiveHolder, "error received in downstream pipeline: " + cause.toString(), distributedSpanStackToUse, mdcContextToUse);
            // No matter what the cause is we want to make sure the channel is closed. Doing this raw ch.close()
            //      here will catch the cases where this channel does not have an active call but still needs to be
            //      closed (e.g. an idle channel timeout that happens in-between calls).
            ch.close();
            // Unhook the tracing and MDC stuff from this thread now that we're done.
            unlinkTracingAndMdcFromCurrentThread(originalThreadInfo);
        }
    };
    ChannelHandler errorHandler = new ChannelInboundHandlerAdapter() {

        @Override
        public void exceptionCaught(ChannelHandlerContext downstreamCallCtx, Throwable cause) throws Exception {
            doErrorHandlingConsumer.accept(cause);
        }

        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            if (logger.isDebugEnabled()) {
                runnableWithTracingAndMdc(() -> logger.debug("Downstream channel closing. call_active={}, last_chunk_sent_downstream={}, channel_id={}", callActiveHolder.heldObject, lastChunkSentDownstreamHolder.heldObject, ctx.channel().toString()), distributedSpanStackToUse, mdcContextToUse).run();
            }
            // We only care if the channel was closed while the call was active.
            if (callActiveHolder.heldObject)
                doErrorHandlingConsumer.accept(new DownstreamChannelClosedUnexpectedlyException(ch));
            super.channelInactive(ctx);
        }
    };
    // Set up the HTTP client pipeline.
    ChannelPipeline p = ch.pipeline();
    List<String> registeredHandlerNames = p.names();
    //      couldn't be removed at that time because it wasn't in the channel's eventLoop.
    if (registeredHandlerNames.contains(DOWNSTREAM_IDLE_CHANNEL_TIMEOUT_HANDLER_NAME)) {
        ChannelHandler idleHandler = p.get(DOWNSTREAM_IDLE_CHANNEL_TIMEOUT_HANDLER_NAME);
        if (idleHandler != null)
            p.remove(idleHandler);
    }
    if (debugChannelLifecycleLoggingEnabled && !registeredHandlerNames.contains(DEBUG_LOGGER_HANDLER_NAME)) {
        // Add the channel debug logger if desired.
        p.addFirst(DEBUG_LOGGER_HANDLER_NAME, new LoggingHandler(DOWNSTREAM_CLIENT_CHANNEL_DEBUG_LOGGER_NAME, LogLevel.DEBUG));
    }
    // Add/replace a downstream call timeout detector.
    addOrReplacePipelineHandler(new DownstreamIdleChannelTimeoutHandler(downstreamCallTimeoutMillis, () -> callActiveHolder.heldObject, true, "StreamingAsyncHttpClientChannel-call-timeout", distributedSpanStackToUse, mdcContextToUse), DOWNSTREAM_CALL_TIMEOUT_HANDLER_NAME, p, registeredHandlerNames);
    if (isSecureHttpsCall) {
        // SSL call. Make sure we add the SSL handler if necessary.
        if (!registeredHandlerNames.contains(SSL_HANDLER_NAME)) {
            if (clientSslCtx == null) {
                if (relaxedHttpsValidation) {
                    clientSslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
                } else {
                    TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
                    tmf.init((KeyStore) null);
                    clientSslCtx = SslContextBuilder.forClient().trustManager(tmf).build();
                }
            }
            p.addAfter(DOWNSTREAM_CALL_TIMEOUT_HANDLER_NAME, SSL_HANDLER_NAME, clientSslCtx.newHandler(ch.alloc()));
        }
    } else {
        // Not an SSL call. Remove the SSL handler if it's there.
        if (registeredHandlerNames.contains(SSL_HANDLER_NAME))
            p.remove(SSL_HANDLER_NAME);
    }
    // The HttpClientCodec handler deals with HTTP codec stuff so you don't have to. Set it up if it hasn't already
    //      been setup, and inspect it to make sure it's in a "ready to handle a new request" state. Some rare
    //      and currently unknown edgecases can cause us to hit this point with the HttpClientCodec in an unclean
    //      state, and if we barrel forward without cleaning this up the call will fail.
    boolean pipelineContainsHttpClientCodec = registeredHandlerNames.contains(HTTP_CLIENT_CODEC_HANDLER_NAME);
    boolean existingHttpClientCodecIsInBadState = false;
    if (pipelineContainsHttpClientCodec) {
        HttpClientCodec currentCodec = (HttpClientCodec) p.get(HTTP_CLIENT_CODEC_HANDLER_NAME);
        int currentHttpClientCodecInboundState = determineHttpClientCodecInboundState(currentCodec);
        if (currentHttpClientCodecInboundState != 0) {
            runnableWithTracingAndMdc(() -> logger.warn("HttpClientCodec inbound state was not 0. It will be replaced with a fresh HttpClientCodec. " + "bad_httpclientcodec_inbound_state={}", currentHttpClientCodecInboundState), distributedSpanStackToUse, mdcContextToUse).run();
            existingHttpClientCodecIsInBadState = true;
        } else {
            int currentHttpClientCodecOutboundState = determineHttpClientCodecOutboundState(currentCodec);
            if (currentHttpClientCodecOutboundState != 0) {
                runnableWithTracingAndMdc(() -> logger.warn("HttpClientCodec outbound state was not 0. It will be replaced with a fresh HttpClientCodec. " + "bad_httpclientcodec_outbound_state={}", currentHttpClientCodecOutboundState), distributedSpanStackToUse, mdcContextToUse).run();
                existingHttpClientCodecIsInBadState = true;
            }
        }
    }
    //      or replace it if it was in a bad state.
    if (!pipelineContainsHttpClientCodec || existingHttpClientCodecIsInBadState) {
        addOrReplacePipelineHandler(new HttpClientCodec(4096, 8192, 8192, true), HTTP_CLIENT_CODEC_HANDLER_NAME, p, registeredHandlerNames);
    }
    // Update the chunk sender handler and error handler to the newly created versions that know about the correct
    //      callback, dtrace info, etc to use for this request.
    addOrReplacePipelineHandler(chunkSenderHandler, CHUNK_SENDER_HANDLER_NAME, p, registeredHandlerNames);
    addOrReplacePipelineHandler(errorHandler, ERROR_HANDLER_NAME, p, registeredHandlerNames);
}
Also used : AttributeKey(io.netty.util.AttributeKey) Span(com.nike.wingtips.Span) HttpHeaders(io.netty.handler.codec.http.HttpHeaders) DefaultThreadFactory(io.netty.util.concurrent.DefaultThreadFactory) ChannelInboundHandlerAdapter(io.netty.channel.ChannelInboundHandlerAdapter) HttpMessage(io.netty.handler.codec.http.HttpMessage) LoggerFactory(org.slf4j.LoggerFactory) TraceHeaders(com.nike.wingtips.TraceHeaders) Random(java.util.Random) KeyStoreException(java.security.KeyStoreException) AsyncNettyHelper.unlinkTracingAndMdcFromCurrentThread(com.nike.riposte.util.AsyncNettyHelper.unlinkTracingAndMdcFromCurrentThread) HttpObject(io.netty.handler.codec.http.HttpObject) HttpClientCodec(io.netty.handler.codec.http.HttpClientCodec) InetAddress(java.net.InetAddress) ChannelPromise(io.netty.channel.ChannelPromise) Map(java.util.Map) ThreadFactory(java.util.concurrent.ThreadFactory) SocketChannel(io.netty.channel.socket.SocketChannel) HttpObjectDecoder(io.netty.handler.codec.http.HttpObjectDecoder) HttpRequest(io.netty.handler.codec.http.HttpRequest) TrustManagerFactory(javax.net.ssl.TrustManagerFactory) DownstreamIdleChannelTimeoutException(com.nike.riposte.server.error.exception.DownstreamIdleChannelTimeoutException) ChannelHealthChecker(io.netty.channel.pool.ChannelHealthChecker) DownstreamChannelClosedUnexpectedlyException(com.nike.riposte.server.error.exception.DownstreamChannelClosedUnexpectedlyException) KeyStore(java.security.KeyStore) ChannelPipeline(io.netty.channel.ChannelPipeline) InetSocketAddress(java.net.InetSocketAddress) NioEventLoopGroup(io.netty.channel.nio.NioEventLoopGroup) List(java.util.List) SSLException(javax.net.ssl.SSLException) AbstractChannelPoolHandler(io.netty.channel.pool.AbstractChannelPoolHandler) LogLevel(io.netty.handler.logging.LogLevel) DefaultHttpResponse(io.netty.handler.codec.http.DefaultHttpResponse) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) HttpObjectEncoder(io.netty.handler.codec.http.HttpObjectEncoder) DefaultFullHttpResponse(io.netty.handler.codec.http.DefaultFullHttpResponse) HttpResponse(io.netty.handler.codec.http.HttpResponse) ChannelPoolMap(io.netty.channel.pool.ChannelPoolMap) NioSocketChannel(io.netty.channel.socket.nio.NioSocketChannel) HttpRequestEncoder(io.netty.handler.codec.http.HttpRequestEncoder) DownstreamIdleChannelTimeoutHandler(com.nike.riposte.client.asynchttp.netty.downstreampipeline.DownstreamIdleChannelTimeoutHandler) ChannelOption(io.netty.channel.ChannelOption) LoggingHandler(io.netty.handler.logging.LoggingHandler) Tracer(com.nike.wingtips.Tracer) CompletableFuture(java.util.concurrent.CompletableFuture) Errors(io.netty.channel.unix.Errors) Deque(java.util.Deque) LastHttpContent(io.netty.handler.codec.http.LastHttpContent) EpollSocketChannel(io.netty.channel.epoll.EpollSocketChannel) AsyncNettyHelper.linkTracingAndMdcToCurrentThread(com.nike.riposte.util.AsyncNettyHelper.linkTracingAndMdcToCurrentThread) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) InsecureTrustManagerFactory(io.netty.handler.ssl.util.InsecureTrustManagerFactory) BiConsumer(java.util.function.BiConsumer) EpollEventLoopGroup(io.netty.channel.epoll.EpollEventLoopGroup) HttpContent(io.netty.handler.codec.http.HttpContent) Attribute(io.netty.util.Attribute) Logger(org.slf4j.Logger) EventLoopGroup(io.netty.channel.EventLoopGroup) CombinedChannelDuplexHandler(io.netty.channel.CombinedChannelDuplexHandler) SslContext(io.netty.handler.ssl.SslContext) Promise(io.netty.util.concurrent.Promise) HostnameResolutionException(com.nike.riposte.server.error.exception.HostnameResolutionException) Field(java.lang.reflect.Field) UnknownHostException(java.net.UnknownHostException) ChannelFuture(io.netty.channel.ChannelFuture) Epoll(io.netty.channel.epoll.Epoll) Consumer(java.util.function.Consumer) Channel(io.netty.channel.Channel) SimpleChannelPool(io.netty.channel.pool.SimpleChannelPool) Bootstrap(io.netty.bootstrap.Bootstrap) FullHttpResponse(io.netty.handler.codec.http.FullHttpResponse) WrapperException(com.nike.backstopper.exception.WrapperException) MDC(org.slf4j.MDC) SimpleChannelInboundHandler(io.netty.channel.SimpleChannelInboundHandler) NativeIoExceptionWrapper(com.nike.riposte.server.error.exception.NativeIoExceptionWrapper) AsyncNettyHelper.runnableWithTracingAndMdc(com.nike.riposte.util.AsyncNettyHelper.runnableWithTracingAndMdc) ChannelPool(io.netty.channel.pool.ChannelPool) SslContextBuilder(io.netty.handler.ssl.SslContextBuilder) ChannelHandler(io.netty.channel.ChannelHandler) Pair(com.nike.internal.util.Pair) AbstractChannelPoolMap(io.netty.channel.pool.AbstractChannelPoolMap) Future(io.netty.util.concurrent.Future) DownstreamIdleChannelTimeoutHandler(com.nike.riposte.client.asynchttp.netty.downstreampipeline.DownstreamIdleChannelTimeoutHandler) LoggingHandler(io.netty.handler.logging.LoggingHandler) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) ChannelHandler(io.netty.channel.ChannelHandler) HttpClientCodec(io.netty.handler.codec.http.HttpClientCodec) Span(com.nike.wingtips.Span) HttpObject(io.netty.handler.codec.http.HttpObject) DefaultFullHttpResponse(io.netty.handler.codec.http.DefaultFullHttpResponse) FullHttpResponse(io.netty.handler.codec.http.FullHttpResponse) Pair(com.nike.internal.util.Pair) SimpleChannelInboundHandler(io.netty.channel.SimpleChannelInboundHandler) DefaultFullHttpResponse(io.netty.handler.codec.http.DefaultFullHttpResponse) NativeIoExceptionWrapper(com.nike.riposte.server.error.exception.NativeIoExceptionWrapper) DefaultHttpResponse(io.netty.handler.codec.http.DefaultHttpResponse) DefaultFullHttpResponse(io.netty.handler.codec.http.DefaultFullHttpResponse) HttpResponse(io.netty.handler.codec.http.HttpResponse) FullHttpResponse(io.netty.handler.codec.http.FullHttpResponse) LastHttpContent(io.netty.handler.codec.http.LastHttpContent) ChannelPipeline(io.netty.channel.ChannelPipeline) Errors(io.netty.channel.unix.Errors) DefaultHttpResponse(io.netty.handler.codec.http.DefaultHttpResponse) TrustManagerFactory(javax.net.ssl.TrustManagerFactory) InsecureTrustManagerFactory(io.netty.handler.ssl.util.InsecureTrustManagerFactory) DownstreamChannelClosedUnexpectedlyException(com.nike.riposte.server.error.exception.DownstreamChannelClosedUnexpectedlyException) DownstreamIdleChannelTimeoutException(com.nike.riposte.server.error.exception.DownstreamIdleChannelTimeoutException) ChannelInboundHandlerAdapter(io.netty.channel.ChannelInboundHandlerAdapter)

Example 52 with HttpResponse

use of io.netty.handler.codec.http.HttpResponse in project riposte by Nike-Inc.

the class ProxyRouterEndpointExecutionHandler method doChannelRead.

@Override
public PipelineContinuationBehavior doChannelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    HttpProcessingState state = ChannelAttributes.getHttpProcessingStateForChannel(ctx).get();
    Endpoint<?> endpoint = state.getEndpointForExecution();
    if (shouldHandleDoChannelReadMessage(msg, endpoint)) {
        ProxyRouterProcessingState proxyRouterState = getOrCreateProxyRouterProcessingState(ctx);
        ProxyRouterEndpoint endpointProxyRouter = ((ProxyRouterEndpoint) endpoint);
        RequestInfo<?> requestInfo = state.getRequestInfo();
        if (msg instanceof HttpRequest) {
            if (requestInfo instanceof RiposteInternalRequestInfo) {
                // Tell this RequestInfo that we'll be managing the release of content chunks, so that when
                //      RequestInfo.releaseAllResources() is called we don't have extra reference count removals.
                ((RiposteInternalRequestInfo) requestInfo).contentChunksWillBeReleasedExternally();
            }
            // We're supposed to start streaming. There may be pre-endpoint-execution validation logic or other work
            //      that needs to happen before the endpoint is executed, so set up the CompletableFuture for the
            //      endpoint call to only execute if the pre-endpoint-execution validation/work chain is successful.
            CompletableFuture<DownstreamRequestFirstChunkInfo> firstChunkFuture = state.getPreEndpointExecutionWorkChain().thenCompose(functionWithTracingAndMdc(aVoid -> endpointProxyRouter.getDownstreamRequestFirstChunkInfo(requestInfo, longRunningTaskExecutor, ctx), ctx));
            Long endpointTimeoutOverride = endpointProxyRouter.completableFutureTimeoutOverrideMillis();
            long callTimeoutValueToUse = (endpointTimeoutOverride == null) ? defaultCompletableFutureTimeoutMillis : endpointTimeoutOverride;
            // When the first chunk is ready, stream it downstream and set up what happens afterward.
            firstChunkFuture.whenComplete((downstreamRequestFirstChunkInfo, throwable) -> {
                Optional<ManualModeTask<HttpResponse>> circuitBreakerManualTask = getCircuitBreaker(downstreamRequestFirstChunkInfo, ctx).map(CircuitBreaker::newManualModeTask);
                StreamingCallback callback = new StreamingCallbackForCtx(ctx, circuitBreakerManualTask, endpointProxyRouter, requestInfo, proxyRouterState);
                if (throwable != null) {
                    // Something blew up trying to determine the first chunk info.
                    callback.unrecoverableErrorOccurred(throwable);
                } else if (!ctx.channel().isOpen()) {
                    // The channel was closed for some reason before we were able to start streaming.
                    String errorMsg = "The channel from the original caller was closed before we could begin the " + "downstream call.";
                    Exception channelClosedException = new RuntimeException(errorMsg);
                    runnableWithTracingAndMdc(() -> logger.warn(errorMsg), ctx).run();
                    callback.unrecoverableErrorOccurred(channelClosedException);
                } else {
                    try {
                        // Ok we have the first chunk info. Start by setting the downstream call info in the request
                        //      info (i.e. for access logs if desired)
                        requestInfo.addRequestAttribute(DOWNSTREAM_CALL_PATH_REQUEST_ATTR_KEY, HttpUtils.extractPath(downstreamRequestFirstChunkInfo.firstChunk.getUri()));
                        // Try our circuit breaker (if we have one).
                        Throwable circuitBreakerException = null;
                        try {
                            circuitBreakerManualTask.ifPresent(ManualModeTask::throwExceptionIfCircuitBreakerIsOpen);
                        } catch (Throwable t) {
                            circuitBreakerException = t;
                        }
                        if (circuitBreakerException == null) {
                            // No circuit breaker, or the breaker is closed. We can now stream the first chunk info.
                            String downstreamHost = downstreamRequestFirstChunkInfo.host;
                            int downstreamPort = downstreamRequestFirstChunkInfo.port;
                            HttpRequest downstreamRequestFirstChunk = downstreamRequestFirstChunkInfo.firstChunk;
                            boolean isSecureHttpsCall = downstreamRequestFirstChunkInfo.isHttps;
                            boolean relaxedHttpsValidation = downstreamRequestFirstChunkInfo.relaxedHttpsValidation;
                            // Tell the proxyRouterState about the streaming callback so that
                            //      callback.unrecoverableErrorOccurred(...) can be called in the case of an error
                            //      on subsequent chunks.
                            proxyRouterState.setStreamingCallback(callback);
                            // Setup the streaming channel future with everything it needs to kick off the
                            //      downstream request.
                            proxyRouterState.setStreamingStartTimeNanos(System.nanoTime());
                            CompletableFuture<StreamingChannel> streamingChannel = streamingAsyncHttpClient.streamDownstreamCall(downstreamHost, downstreamPort, downstreamRequestFirstChunk, isSecureHttpsCall, relaxedHttpsValidation, callback, callTimeoutValueToUse, ctx);
                            // Tell the streaming channel future what to do when it completes.
                            streamingChannel = streamingChannel.whenComplete((sc, cause) -> {
                                if (cause == null) {
                                    // Successfully connected and sent the first chunk. We can now safely let
                                    //      the remaining content chunks through for streaming.
                                    proxyRouterState.triggerChunkProcessing(sc);
                                } else {
                                    // Something blew up while connecting to the downstream server.
                                    callback.unrecoverableErrorOccurred(cause);
                                }
                            });
                            // Set the streaming channel future on the state so it can be connected to.
                            proxyRouterState.setStreamingChannelCompletableFuture(streamingChannel);
                        } else {
                            // Circuit breaker is tripped (or otherwise threw an unexpected exception). Immediately
                            //      short circuit the error back to the client.
                            callback.unrecoverableErrorOccurred(circuitBreakerException);
                        }
                    } catch (Throwable t) {
                        callback.unrecoverableErrorOccurred(t);
                    }
                }
            });
        } else if (msg instanceof HttpContent) {
            HttpContent msgContent = (HttpContent) msg;
            //      chunk-streaming behavior and subsequent cleanup for the given HttpContent.
            if (!releaseContentChunkIfStreamAlreadyFailed(msgContent, proxyRouterState)) {
                registerChunkStreamingAction(proxyRouterState, msgContent, ctx);
            }
        }
        return PipelineContinuationBehavior.DO_NOT_FIRE_CONTINUE_EVENT;
    }
    return PipelineContinuationBehavior.CONTINUE;
}
Also used : HttpRequest(io.netty.handler.codec.http.HttpRequest) Span(com.nike.wingtips.Span) LoggerFactory(org.slf4j.LoggerFactory) ResponseInfo(com.nike.riposte.server.http.ResponseInfo) HttpObject(io.netty.handler.codec.http.HttpObject) ProxyRouterEndpoint(com.nike.riposte.server.http.ProxyRouterEndpoint) Map(java.util.Map) HttpRequest(io.netty.handler.codec.http.HttpRequest) CompletionException(java.util.concurrent.CompletionException) EventLoop(io.netty.channel.EventLoop) DownstreamRequestFirstChunkInfo(com.nike.riposte.server.http.ProxyRouterEndpoint.DownstreamRequestFirstChunkInfo) BaseInboundHandlerWithTracingAndMdcSupport(com.nike.riposte.server.handler.base.BaseInboundHandlerWithTracingAndMdcSupport) Endpoint(com.nike.riposte.server.http.Endpoint) HttpUtils(com.nike.riposte.util.HttpUtils) StreamingChannel(com.nike.riposte.client.asynchttp.netty.StreamingAsyncHttpClient.StreamingChannel) ChannelAttributes(com.nike.riposte.server.channelpipeline.ChannelAttributes) CircuitBreaker(com.nike.fastbreak.CircuitBreaker) RiposteInternalRequestInfo(com.nike.riposte.server.http.impl.RiposteInternalRequestInfo) Optional(java.util.Optional) HttpResponse(io.netty.handler.codec.http.HttpResponse) StreamingCallback(com.nike.riposte.client.asynchttp.netty.StreamingAsyncHttpClient.StreamingCallback) HttpProcessingState(com.nike.riposte.server.http.HttpProcessingState) EventExecutor(io.netty.util.concurrent.EventExecutor) RequestInfo(com.nike.riposte.server.http.RequestInfo) CircuitBreakerDelegate(com.nike.fastbreak.CircuitBreakerDelegate) ManualModeTask(com.nike.fastbreak.CircuitBreaker.ManualModeTask) CompletableFuture(java.util.concurrent.CompletableFuture) StreamingAsyncHttpClient(com.nike.riposte.client.asynchttp.netty.StreamingAsyncHttpClient) PipelineContinuationBehavior(com.nike.riposte.server.handler.base.PipelineContinuationBehavior) Deque(java.util.Deque) LastHttpContent(io.netty.handler.codec.http.LastHttpContent) AsyncNettyHelper(com.nike.riposte.util.AsyncNettyHelper) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) HttpContent(io.netty.handler.codec.http.HttpContent) Attribute(io.netty.util.Attribute) OutboundMessageSendHeadersChunkFromResponseInfo(com.nike.riposte.server.channelpipeline.message.OutboundMessageSendHeadersChunkFromResponseInfo) Logger(org.slf4j.Logger) Executor(java.util.concurrent.Executor) AsyncNettyHelper.executeOnlyIfChannelIsActive(com.nike.riposte.util.AsyncNettyHelper.executeOnlyIfChannelIsActive) CircuitBreakerForHttpStatusCode.getDefaultHttpStatusCodeCircuitBreakerForKey(com.nike.fastbreak.CircuitBreakerForHttpStatusCode.getDefaultHttpStatusCodeCircuitBreakerForKey) AsyncNettyHelper.functionWithTracingAndMdc(com.nike.riposte.util.AsyncNettyHelper.functionWithTracingAndMdc) ChannelFuture(io.netty.channel.ChannelFuture) ExecutionException(java.util.concurrent.ExecutionException) FullHttpResponse(io.netty.handler.codec.http.FullHttpResponse) WrapperException(com.nike.backstopper.exception.WrapperException) OutboundMessageSendContentChunk(com.nike.riposte.server.channelpipeline.message.OutboundMessageSendContentChunk) AsyncNettyHelper.runnableWithTracingAndMdc(com.nike.riposte.util.AsyncNettyHelper.runnableWithTracingAndMdc) LastOutboundMessageSendLastContentChunk(com.nike.riposte.server.channelpipeline.message.LastOutboundMessageSendLastContentChunk) Pair(com.nike.internal.util.Pair) ProxyRouterProcessingState(com.nike.riposte.server.http.ProxyRouterProcessingState) CircuitBreaker(com.nike.fastbreak.CircuitBreaker) StreamingCallback(com.nike.riposte.client.asynchttp.netty.StreamingAsyncHttpClient.StreamingCallback) HttpProcessingState(com.nike.riposte.server.http.HttpProcessingState) ProxyRouterProcessingState(com.nike.riposte.server.http.ProxyRouterProcessingState) DownstreamRequestFirstChunkInfo(com.nike.riposte.server.http.ProxyRouterEndpoint.DownstreamRequestFirstChunkInfo) CompletionException(java.util.concurrent.CompletionException) ExecutionException(java.util.concurrent.ExecutionException) WrapperException(com.nike.backstopper.exception.WrapperException) CompletableFuture(java.util.concurrent.CompletableFuture) ManualModeTask(com.nike.fastbreak.CircuitBreaker.ManualModeTask) ProxyRouterEndpoint(com.nike.riposte.server.http.ProxyRouterEndpoint) RiposteInternalRequestInfo(com.nike.riposte.server.http.impl.RiposteInternalRequestInfo) LastHttpContent(io.netty.handler.codec.http.LastHttpContent) HttpContent(io.netty.handler.codec.http.HttpContent)

Example 53 with HttpResponse

use of io.netty.handler.codec.http.HttpResponse in project riposte by Nike-Inc.

the class ProcessFinalResponseOutputHandler method write.

@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
    // Deal with the final outbound HttpResponse
    if (msg instanceof HttpResponse) {
        HttpProcessingState state = ChannelAttributes.getHttpProcessingStateForChannel(ctx).get();
        if (state != null)
            state.setActualResponseObject((HttpResponse) msg);
    }
    // Deal with the final outbound body content
    if (msg instanceof HttpContent) {
        HttpProcessingState state = ChannelAttributes.getHttpProcessingStateForChannel(ctx).get();
        if (state != null && state.getResponseInfo() != null) {
            ResponseInfo<?> responseInfo = state.getResponseInfo();
            long contentBytes = ((HttpContent) msg).content().readableBytes();
            if (responseInfo.getFinalContentLength() == null)
                responseInfo.setFinalContentLength(contentBytes);
            else
                responseInfo.setFinalContentLength(responseInfo.getFinalContentLength() + contentBytes);
        }
    }
    super.write(ctx, msg, promise);
}
Also used : HttpProcessingState(com.nike.riposte.server.http.HttpProcessingState) HttpResponse(io.netty.handler.codec.http.HttpResponse) HttpContent(io.netty.handler.codec.http.HttpContent)

Example 54 with HttpResponse

use of io.netty.handler.codec.http.HttpResponse in project riposte by Nike-Inc.

the class SmartHttpContentCompressor method write.

@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
    HttpProcessingState state = ChannelAttributes.getHttpProcessingStateForChannel(ctx).get();
    allowCompressionForThisRequest = false;
    if (state != null) {
        // We only want to allow compression if the endpoint being hit is *not* a ProxyRouterEndpoint, the response is full, and the response size
        // is greater than the threshold
        boolean isFull = msg instanceof HttpResponse && msg instanceof LastHttpContent;
        boolean endpointAllowed = endpointAllowsCompression(state.getEndpointForExecution());
        boolean responseInfoAllowed = state.getResponseInfo() == null || !state.getResponseInfo().isPreventCompressedOutput();
        if (isFull && endpointAllowed && responseInfoAllowed && ((LastHttpContent) msg).content().readableBytes() > responseSizeThresholdBytes) {
            allowCompressionForThisRequest = true;
        }
    }
    super.write(ctx, msg, promise);
}
Also used : HttpProcessingState(com.nike.riposte.server.http.HttpProcessingState) HttpResponse(io.netty.handler.codec.http.HttpResponse) LastHttpContent(io.netty.handler.codec.http.LastHttpContent)

Example 55 with HttpResponse

use of io.netty.handler.codec.http.HttpResponse in project riposte by Nike-Inc.

the class ProcessFinalResponseOutputHandlerTest method write_calls_setActualResponseObject_on_state_if_msg_is_HttpResponse.

@Test
public void write_calls_setActualResponseObject_on_state_if_msg_is_HttpResponse() throws Exception {
    // given
    HttpResponse msgMock = mock(HttpResponse.class);
    // when
    handler.write(ctxMock, msgMock, promiseMock);
    // then
    verify(stateMock).setActualResponseObject(msgMock);
}
Also used : HttpResponse(io.netty.handler.codec.http.HttpResponse) Test(org.junit.Test)

Aggregations

HttpResponse (io.netty.handler.codec.http.HttpResponse)127 DefaultFullHttpResponse (io.netty.handler.codec.http.DefaultFullHttpResponse)57 Test (org.junit.Test)50 EmbeddedChannel (io.netty.channel.embedded.EmbeddedChannel)30 HttpRequest (io.netty.handler.codec.http.HttpRequest)27 DefaultHttpResponse (io.netty.handler.codec.http.DefaultHttpResponse)26 FullHttpResponse (io.netty.handler.codec.http.FullHttpResponse)24 HttpContent (io.netty.handler.codec.http.HttpContent)18 LastHttpContent (io.netty.handler.codec.http.LastHttpContent)18 FullHttpRequest (io.netty.handler.codec.http.FullHttpRequest)14 ChannelFuture (io.netty.channel.ChannelFuture)12 DefaultFullHttpRequest (io.netty.handler.codec.http.DefaultFullHttpRequest)12 ByteBuf (io.netty.buffer.ByteBuf)10 AsciiString (io.netty.util.AsciiString)9 HttpObject (io.netty.handler.codec.http.HttpObject)7 Map (java.util.Map)7 CoreMatchers.containsString (org.hamcrest.CoreMatchers.containsString)7 WebSocketExtensionData (io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionData)6 IOException (java.io.IOException)6 Settings (org.elasticsearch.common.settings.Settings)6