Search in sources :

Example 1 with ProxyRouterProcessingState

use of com.nike.riposte.server.http.ProxyRouterProcessingState in project riposte by Nike-Inc.

the class ProxyRouterEndpointExecutionHandler method doChannelRead.

@Override
public PipelineContinuationBehavior doChannelRead(ChannelHandlerContext ctx, Object msg) {
    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, true);
                } 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, true);
                } 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.uri()));
                        // 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;
                            boolean performSubSpanAroundDownstreamCall = downstreamRequestFirstChunkInfo.performSubSpanAroundDownstreamCall;
                            boolean addTracingHeadersToDownstreamCall = downstreamRequestFirstChunkInfo.addTracingHeadersToDownstreamCall;
                            // 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, performSubSpanAroundDownstreamCall, addTracingHeadersToDownstreamCall, proxyRouterState, requestInfo, 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, true);
                                }
                            });
                            // 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, true);
                        }
                    } catch (Throwable t) {
                        callback.unrecoverableErrorOccurred(t, true);
                    }
                }
            });
        } 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) DistributedTracingConfig(com.nike.riposte.server.config.distributedtracing.DistributedTracingConfig) 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 2 with ProxyRouterProcessingState

use of com.nike.riposte.server.http.ProxyRouterProcessingState in project riposte by Nike-Inc.

the class ProxyRouterEndpointExecutionHandler method getOrCreateProxyRouterProcessingState.

protected ProxyRouterProcessingState getOrCreateProxyRouterProcessingState(ChannelHandlerContext ctx) {
    Attribute<ProxyRouterProcessingState> proxyRouterStateAttribute = ChannelAttributes.getProxyRouterProcessingStateForChannel(ctx);
    ProxyRouterProcessingState proxyRouterState = proxyRouterStateAttribute.get();
    if (proxyRouterState == null) {
        proxyRouterState = new ProxyRouterProcessingState();
        proxyRouterStateAttribute.set(proxyRouterState);
        // noinspection deprecation
        proxyRouterState.setDistributedTracingConfig(distributedTracingConfig);
    }
    return proxyRouterState;
}
Also used : ProxyRouterProcessingState(com.nike.riposte.server.http.ProxyRouterProcessingState)

Example 3 with ProxyRouterProcessingState

use of com.nike.riposte.server.http.ProxyRouterProcessingState in project riposte by Nike-Inc.

the class RequestStateCleanerHandler method channelRead.

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    if (msg instanceof HttpRequest) {
        // New request incoming - setup/clear *all* state objects for new requests
        for (ProcessingStateClassAndKeyPair<? extends ProcessingState> stateClassAndKeyPair : PROCESSING_STATE_ATTRIBUTE_KEYS) {
            // See if we have an existing state object for this channel for the given state type.
            @SuppressWarnings("unchecked") AttributeKey<ProcessingState> attrKey = (AttributeKey<ProcessingState>) stateClassAndKeyPair.getRight();
            Attribute<ProcessingState> processingStateAttr = ctx.channel().attr(attrKey);
            ProcessingState processingState = processingStateAttr.get();
            if (processingState == null) {
                // We don't already have one for this channel, so create one and register it.
                processingState = stateClassAndKeyPair.getLeft().newInstance();
                processingStateAttr.set(processingState);
            }
            // Clean the state for the new request.
            processingState.cleanStateForNewRequest();
        }
        HttpProcessingState httpProcessingState = ChannelAttributes.getHttpProcessingStateForChannel(ctx).get();
        // Set the DistributedTracingConfig on the HttpProcessingState.
        // noinspection deprecation - This is the only place that should actually be calling this method.
        httpProcessingState.setDistributedTracingConfig(distributedTracingConfig);
        // Send a request received event to the metricsListener.
        if (metricsListener != null) {
            metricsListener.onEvent(ServerMetricsEvent.REQUEST_RECEIVED, httpProcessingState);
        }
        // Remove the idle channel timeout handler (if there is one) so that it doesn't kill this new request if the
        // endpoint takes longer to complete than the idle timeout value - the idle channel timeout is only for
        // timing out channels that are idle *in-between* requests.
        ChannelPipeline pipeline = ctx.pipeline();
        ChannelHandler idleChannelTimeoutHandler = pipeline.get(HttpChannelInitializer.IDLE_CHANNEL_TIMEOUT_HANDLER_NAME);
        if (idleChannelTimeoutHandler != null)
            pipeline.remove(idleChannelTimeoutHandler);
        // last chunk when the timeout hits.
        if (incompleteHttpCallTimeoutMillis > 0 && !(msg instanceof LastHttpContent)) {
            IncompleteHttpCallTimeoutHandler newHandler = new IncompleteHttpCallTimeoutHandler(incompleteHttpCallTimeoutMillis);
            ChannelHandler existingHandler = pipeline.get(INCOMPLETE_HTTP_CALL_TIMEOUT_HANDLER_NAME);
            if (existingHandler == null) {
                pipeline.addFirst(INCOMPLETE_HTTP_CALL_TIMEOUT_HANDLER_NAME, newHandler);
            } else {
                logger.error("Handling HttpRequest for new request and found an IncompleteHttpCallTimeoutHandler " + "already in the pipeline. This should not be possible. A new " + "IncompleteHttpCallTimeoutHandler will replace the old one. worker_channel_id={}", ctx.channel().toString());
                pipeline.replace(existingHandler, INCOMPLETE_HTTP_CALL_TIMEOUT_HANDLER_NAME, newHandler);
            }
        }
        ProxyRouterProcessingState proxyRouterProcessingState = ChannelAttributes.getProxyRouterProcessingStateForChannel(ctx).get();
        // Set the DistributedTracingConfig on the ProxyRouterProcessingState.
        // noinspection deprecation - This is the only place that should actually be calling this method.
        proxyRouterProcessingState.setDistributedTracingConfig(distributedTracingConfig);
    } else if (msg instanceof LastHttpContent) {
        // The HTTP call is complete, so we can remove the IncompleteHttpCallTimeoutHandler.
        ChannelPipeline pipeline = ctx.pipeline();
        ChannelHandler existingHandler = pipeline.get(INCOMPLETE_HTTP_CALL_TIMEOUT_HANDLER_NAME);
        if (existingHandler != null)
            pipeline.remove(INCOMPLETE_HTTP_CALL_TIMEOUT_HANDLER_NAME);
    }
    // Continue on the pipeline processing.
    super.channelRead(ctx, msg);
}
Also used : HttpRequest(io.netty.handler.codec.http.HttpRequest) HttpProcessingState(com.nike.riposte.server.http.HttpProcessingState) ProxyRouterProcessingState(com.nike.riposte.server.http.ProxyRouterProcessingState) ChannelHandler(io.netty.channel.ChannelHandler) LastHttpContent(io.netty.handler.codec.http.LastHttpContent) ChannelPipeline(io.netty.channel.ChannelPipeline) AttributeKey(io.netty.util.AttributeKey) HttpProcessingState(com.nike.riposte.server.http.HttpProcessingState) ProcessingState(com.nike.riposte.server.http.ProcessingState) ProxyRouterProcessingState(com.nike.riposte.server.http.ProxyRouterProcessingState)

Example 4 with ProxyRouterProcessingState

use of com.nike.riposte.server.http.ProxyRouterProcessingState in project riposte by Nike-Inc.

the class ResponseSenderHandler method sendResponse.

protected void sendResponse(ChannelHandlerContext ctx, Object msg, boolean sendLastDitchResponseInline) throws JsonProcessingException {
    try {
        // Try to send the response.
        doSendResponse(ctx, msg);
    } catch (Exception origSendEx) {
        boolean shouldRethrowOriginalSendEx = true;
        // Something went wrong while trying to send the response. We want to create a generic service error and
        // send that back to the caller if possible.
        // The HttpProcessingState will never be null thanks to ExceptionHandlingHandler
        HttpProcessingState state = ChannelAttributes.getHttpProcessingStateForChannel(ctx).get();
        boolean alreadyTriedSendingLastDitchResponse = alreadyTriedSendingLastDitchResponse(state);
        // Cancel any proxy router streaming that may be happening.
        ProxyRouterProcessingState proxyRouterProcessingState = ChannelAttributes.getProxyRouterProcessingStateForChannel(ctx).get();
        if (proxyRouterProcessingState != null) {
            proxyRouterProcessingState.cancelRequestStreaming(origSendEx, ctx);
            proxyRouterProcessingState.cancelDownstreamRequest(origSendEx);
        }
        // we can create a generic service error and try sending that.
        if (state.isResponseSendingStarted()) {
            runnableWithTracingAndMdc(() -> {
                logger.error("An unexpected error occurred while sending the response. At least part of the " + "response was sent, so there's nothing we can do at this point but close the connection.", origSendEx);
                // Add this error to the current span if possible, but only if no error tag already exists.
                Span currentSpan = Tracer.getInstance().getCurrentSpan();
                if (currentSpan != null && currentSpan.getTags().get(KnownZipkinTags.ERROR) == null) {
                    String errorTagValue = (origSendEx.getMessage() == null) ? origSendEx.getClass().getSimpleName() : origSendEx.getMessage();
                    currentSpan.putTag(KnownZipkinTags.ERROR, errorTagValue);
                }
            }, ctx).run();
            ctx.channel().close();
        } else if (!alreadyTriedSendingLastDitchResponse) {
            // Mark that we've tried doing the last-ditch response so that we only ever attempt it once.
            markTriedSendingLastDitchResponse(state);
            // We haven't already started response sending, so we can try sending a last ditch error response
            // instead that represents the response-sending exception.
            String errorId = UUID.randomUUID().toString();
            ResponseInfo<?> lastDitchErrorResponseInfo = ResponseInfo.newBuilder(new ErrorResponseBodyImpl(errorId, Collections.singleton(SampleCoreApiError.GENERIC_SERVICE_ERROR))).withHeaders(new DefaultHttpHeaders().set("error_uid", errorId)).withHttpStatusCode(500).build();
            state.setResponseInfo(lastDitchErrorResponseInfo, origSendEx);
            runnableWithTracingAndMdc(() -> logger.error("An unexpected error occurred while attempting to send a response. We'll attempt to send a " + "last-ditch error message. error_uid={}", errorId, origSendEx), ctx).run();
            if (sendLastDitchResponseInline) {
                doSendResponse(ctx, msg);
                // The last ditch response was successfully sent - we don't need to rethrow the original exception.
                shouldRethrowOriginalSendEx = false;
            }
        }
        if (shouldRethrowOriginalSendEx) {
            // will be allowed to propagate out of this class.
            throw origSendEx;
        }
    }
}
Also used : ErrorResponseBodyImpl(com.nike.backstopper.model.riposte.ErrorResponseBodyImpl) DefaultHttpHeaders(io.netty.handler.codec.http.DefaultHttpHeaders) HttpProcessingState(com.nike.riposte.server.http.HttpProcessingState) ProxyRouterProcessingState(com.nike.riposte.server.http.ProxyRouterProcessingState) Span(com.nike.wingtips.Span) JsonProcessingException(com.fasterxml.jackson.core.JsonProcessingException)

Example 5 with ProxyRouterProcessingState

use of com.nike.riposte.server.http.ProxyRouterProcessingState in project riposte by Nike-Inc.

the class ChannelPipelineFinalizerHandler method doChannelInactive.

/**
 * This method is used as the final cleanup safety net for when a channel is closed. It guarantees that any
 * {@link ByteBuf}s being held by {@link RequestInfo} or {@link ProxyRouterProcessingState} are {@link
 * ByteBuf#release()}d so that we don't end up with a memory leak.
 *
 * <p>Note that we can't use {@link ChannelOutboundHandler#close(ChannelHandlerContext, ChannelPromise)} for this
 * purpose as it is only called if we close the connection in our application code. It won't be triggered if
 * (for example) the caller closes the connection, and we need it to *always* run for *every* closed connection,
 * no matter the source of the close. {@link ChannelInboundHandler#channelInactive(ChannelHandlerContext)} is always
 * called, so we're using that.
 */
@Override
public PipelineContinuationBehavior doChannelInactive(ChannelHandlerContext ctx) throws Exception {
    try {
        // Grab hold of the things we may need when cleaning up.
        HttpProcessingState httpState = ChannelAttributes.getHttpProcessingStateForChannel(ctx).get();
        ProxyRouterProcessingState proxyRouterState = ChannelAttributes.getProxyRouterProcessingStateForChannel(ctx).get();
        if (httpState == null) {
            if (proxyRouterState == null) {
                logger.debug("This channel closed before it processed any requests. Nothing to cleanup. " + "current_span={}", Tracer.getInstance().getCurrentSpan());
            } else {
                // This should never happen, but if it does we'll try to release what we can and then return.
                logger.error("Found a channel where HttpProcessingState was null, but ProxyRouterProcessingState " + "was not null. This should not be possible! " + "current_span={}", Tracer.getInstance().getCurrentSpan());
                releaseProxyRouterStateResources(proxyRouterState, ctx);
            }
            // With httpState null, there's nothing left for us to do.
            return PipelineContinuationBehavior.CONTINUE;
        }
        RequestInfo<?> requestInfo = httpState.getRequestInfo();
        ResponseInfo<?> responseInfo = httpState.getResponseInfo();
        if (logger.isDebugEnabled()) {
            runnableWithTracingAndMdc(() -> logger.debug("Cleaning up channel after it was closed. closed_channel_id={}", ctx.channel().toString()), ctx).run();
        }
        // The request/response is definitely done at this point since the channel is closing. Set the response end
        // time if it hasn't already been done.
        httpState.setResponseEndTimeNanosToNowIfNotAlreadySet();
        // Handle the case where the response wasn't fully sent or tracing wasn't completed for some reason.
        // We want to finish the distributed tracing span for this request since there's no other place it
        // might be done, and if the request wasn't fully sent then we should spit out a log message so
        // debug investigations can find out what happened.
        @SuppressWarnings("SimplifiableConditionalExpression") boolean tracingAlreadyCompleted = httpState.isTraceCompletedOrScheduled();
        boolean responseNotFullySent = responseInfo == null || !responseInfo.isResponseSendingLastChunkSent();
        if (responseNotFullySent || !tracingAlreadyCompleted) {
            try {
                runnableWithTracingAndMdc(() -> {
                    if (responseNotFullySent) {
                        logger.warn("The caller's channel was closed before a response could be sent. Distributed tracing " + "will be completed now if it wasn't already done, and we will attempt to output an " + "access log if needed. Any dangling resources will be released. " + "response_info_is_null={}", (responseInfo == null));
                    }
                    if (!tracingAlreadyCompleted) {
                        httpState.setTraceCompletedOrScheduled(true);
                        httpState.handleTracingResponseTaggingAndFinalSpanNameIfNotAlreadyDone();
                        Span currentSpan = Tracer.getInstance().getCurrentSpan();
                        if (currentSpan != null && !currentSpan.isCompleted()) {
                            Tracer.getInstance().completeRequestSpan();
                        }
                    }
                }, ctx).run();
            } catch (Throwable t) {
                logErrorWithTracing("An unexpected error occurred while trying to finalize distributed tracing. " + "This exception will be swallowed.", t, httpState);
            }
        }
        // Make sure access logging is handled
        try {
            if (!httpState.isAccessLogCompletedOrScheduled() && accessLogger != null) {
                httpState.setAccessLogCompletedOrScheduled(true);
                RequestInfo<?> requestInfoToUse = (requestInfo == null) ? RequestInfoImpl.dummyInstanceForUnknownRequests() : requestInfo;
                accessLogger.log(requestInfoToUse, httpState.getActualResponseObject(), responseInfo, httpState.calculateTotalRequestTimeMillis());
            }
        } catch (Throwable t) {
            logErrorWithTracing("An unexpected error occurred while trying to finalize access logging. " + "This exception will be swallowed.", t, httpState);
        }
        // Make sure metrics is handled
        handleMetricsForCompletedRequestIfNotAlreadyDone(httpState);
        // Tell the RequestInfo it can release all its resources.
        if (requestInfo != null) {
            try {
                requestInfo.releaseAllResources();
            } catch (Throwable t) {
                logErrorWithTracing("An unexpected error occurred while trying to release request resources. " + "This exception will be swallowed.", t, httpState);
            }
        }
        try {
            releaseProxyRouterStateResources(proxyRouterState, ctx);
        } catch (Throwable t) {
            logErrorWithTracing("An unexpected error occurred while trying to release ProxyRouter state resources. " + "This exception will be swallowed.", t, httpState);
        }
    } catch (Throwable t) {
        logErrorWithTracing("An unexpected error occurred during ChannelPipelineFinalizerHandler.doChannelInactive() - this " + "should not happen and indicates a bug that needs to be fixed in Riposte.", t, ctx);
    }
    return PipelineContinuationBehavior.CONTINUE;
}
Also used : HttpProcessingState(com.nike.riposte.server.http.HttpProcessingState) ProxyRouterProcessingState(com.nike.riposte.server.http.ProxyRouterProcessingState) Span(com.nike.wingtips.Span)

Aggregations

ProxyRouterProcessingState (com.nike.riposte.server.http.ProxyRouterProcessingState)13 HttpProcessingState (com.nike.riposte.server.http.HttpProcessingState)9 ChannelHandlerContext (io.netty.channel.ChannelHandlerContext)6 Span (com.nike.wingtips.Span)5 DistributedTracingConfig (com.nike.riposte.server.config.distributedtracing.DistributedTracingConfig)4 Channel (io.netty.channel.Channel)4 WrapperException (com.nike.backstopper.exception.WrapperException)3 Pair (com.nike.internal.util.Pair)3 RequestInfo (com.nike.riposte.server.http.RequestInfo)3 AsyncNettyHelper.runnableWithTracingAndMdc (com.nike.riposte.util.AsyncNettyHelper.runnableWithTracingAndMdc)3 ChannelFuture (io.netty.channel.ChannelFuture)3 Attribute (io.netty.util.Attribute)3 StringUtils (com.nike.internal.util.StringUtils)2 StreamingChannel (com.nike.riposte.client.asynchttp.netty.StreamingAsyncHttpClient.StreamingChannel)2 DownstreamIdleChannelTimeoutHandler (com.nike.riposte.client.asynchttp.netty.downstreampipeline.DownstreamIdleChannelTimeoutHandler)2 ProxyRouterSpanNamingAndTaggingStrategy (com.nike.riposte.server.config.distributedtracing.ProxyRouterSpanNamingAndTaggingStrategy)2 DownstreamChannelClosedUnexpectedlyException (com.nike.riposte.server.error.exception.DownstreamChannelClosedUnexpectedlyException)2 DownstreamIdleChannelTimeoutException (com.nike.riposte.server.error.exception.DownstreamIdleChannelTimeoutException)2 HostnameResolutionException (com.nike.riposte.server.error.exception.HostnameResolutionException)2 NativeIoExceptionWrapper (com.nike.riposte.server.error.exception.NativeIoExceptionWrapper)2