Search in sources :

Example 11 with HttpProcessingState

use of com.nike.riposte.server.http.HttpProcessingState 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 12 with HttpProcessingState

use of com.nike.riposte.server.http.HttpProcessingState 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);
            }
            // 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();
        }
        // 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.
        // TODO: Is there a way we can handle access logging and/or metrics here, but only if it wasn't done elsewhere?
        @SuppressWarnings("SimplifiableConditionalExpression") boolean tracingAlreadyCompleted = httpState.isTraceCompletedOrScheduled();
        boolean responseNotFullySent = responseInfo == null || !responseInfo.isResponseSendingLastChunkSent();
        if (responseNotFullySent || !tracingAlreadyCompleted) {
            runnableWithTracingAndMdc(() -> {
                if (responseNotFullySent) {
                    logger.warn("The caller's channel was closed before a response could be sent. This means that " + "an access log probably does not exist for this request. Distributed tracing " + "will be completed now if it wasn't already done. Any dangling resources will be " + "released. response_info_is_null={}", (responseInfo == null));
                }
                if (!tracingAlreadyCompleted) {
                    Span currentSpan = Tracer.getInstance().getCurrentSpan();
                    if (currentSpan != null && !currentSpan.isCompleted())
                        Tracer.getInstance().completeRequestSpan();
                    httpState.setTraceCompletedOrScheduled(true);
                }
            }, ctx).run();
        }
        // Tell the RequestInfo it can release all its resources.
        if (requestInfo != null)
            requestInfo.releaseAllResources();
        releaseProxyRouterStateResources(proxyRouterState);
    } catch (Throwable t) {
        runnableWithTracingAndMdc(() -> logger.error("An unexpected error occurred during ChannelPipelineFinalizerHandler.doChannelInactive() - this " + "should not happen and indicates a bug that needs to be fixed in Riposte.", t), ctx).run();
    }
    return PipelineContinuationBehavior.CONTINUE;
}
Also used : HttpProcessingState(com.nike.riposte.server.http.HttpProcessingState) ProxyRouterProcessingState(com.nike.riposte.server.http.ProxyRouterProcessingState) Span(com.nike.wingtips.Span)

Example 13 with HttpProcessingState

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

the class RequestContentValidationHandler method doChannelRead.

@Override
public PipelineContinuationBehavior doChannelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    if (msg instanceof LastHttpContent) {
        HttpProcessingState state = ChannelAttributes.getHttpProcessingStateForChannel(ctx).get();
        Endpoint<?> endpoint = state.getEndpointForExecution();
        RequestInfo<?> requestInfo = state.getRequestInfo();
        if (endpoint != null && endpoint.isValidateRequestContent(requestInfo) && // Request must be complete
        requestInfo.isCompleteRequestWithAllChunks() && // Content deserialization must be possible
        requestInfo.isContentDeserializerSetup() && // Must have something to validate - TODO: This last rule for non-empty content might change depending on what we do about auto-validating null content.
        requestInfo.getRawContentLengthInBytes() > 0) {
            if (endpoint.shouldValidateAsynchronously(requestInfo)) {
                // The endpoint has requested asynchronous validation, so split it off into the
                //      pre-endpoint-execution-work-chain.
                state.addPreEndpointExecutionWorkChainSegment(aVoid -> CompletableFuture.runAsync(() -> executeValidation(requestInfo, endpoint, ctx), ASYNC_VALIDATION_EXECUTOR));
            } else {
                // This request can be validated synchronously, so do it now.
                executeValidation(requestInfo, endpoint, ctx);
            }
        }
    }
    return PipelineContinuationBehavior.CONTINUE;
}
Also used : HttpProcessingState(com.nike.riposte.server.http.HttpProcessingState) LastHttpContent(io.netty.handler.codec.http.LastHttpContent)

Example 14 with HttpProcessingState

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

the class RequestHasBeenHandledVerificationHandler method doChannelRead.

@Override
public PipelineContinuationBehavior doChannelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    if (msg == null) {
        throw new InvalidRipostePipelineException("msg cannot be null at this stage of the pipeline. An endpoint handler should have fired a valid " + "OutboundMessage. invalid_riposte_pipeline=true");
    }
    if (!(msg instanceof OutboundMessage)) {
        throw new InvalidRipostePipelineException("Expected msg to be a OutboundMessage, but instead found: " + msg.getClass().getName() + ". invalid_riposte_pipeline=true");
    }
    HttpProcessingState state = ChannelAttributes.getHttpProcessingStateForChannel(ctx).get();
    if (state == null) {
        throw new InvalidRipostePipelineException("Found null HttpProcessingState in the channel, which is not allowed at this point. " + "invalid_riposte_pipeline=true");
    }
    ResponseInfo<?> responseInfo = state.getResponseInfo();
    if (responseInfo == null) {
        throw new InvalidRipostePipelineException("Found null ResponseInfo in the channel state, which is not allowed at this point. " + "An endpoint handler should have set a ResponseInfo on the state. invalid_riposte_pipeline=true");
    }
    if (responseInfo.isChunkedResponse() && !(msg instanceof ChunkedOutboundMessage)) {
        throw new InvalidRipostePipelineException("ResponseInfo.isChunkedResponse() indicates a chunked response, but the message was not a " + "ChunkedOutboundMessage. msg_type=" + msg.getClass().getName() + ", invalid_riposte_pipeline=true");
    }
    if (!responseInfo.isChunkedResponse() && !(msg instanceof LastOutboundMessageSendFullResponseInfo)) {
        throw new InvalidRipostePipelineException("ResponseInfo.isChunkedResponse() indicates a full response, but the message was not a " + "LastOutboundMessageSendFullResponseInfo. msg_type=" + msg.getClass().getName() + ", invalid_riposte_pipeline=true");
    }
    return CONTINUE;
}
Also used : HttpProcessingState(com.nike.riposte.server.http.HttpProcessingState) ChunkedOutboundMessage(com.nike.riposte.server.channelpipeline.message.ChunkedOutboundMessage) OutboundMessage(com.nike.riposte.server.channelpipeline.message.OutboundMessage) InvalidRipostePipelineException(com.nike.riposte.server.error.exception.InvalidRipostePipelineException) ChunkedOutboundMessage(com.nike.riposte.server.channelpipeline.message.ChunkedOutboundMessage) LastOutboundMessageSendFullResponseInfo(com.nike.riposte.server.channelpipeline.message.LastOutboundMessageSendFullResponseInfo)

Example 15 with HttpProcessingState

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

the class AsyncHttpClientHelper method executeAsyncHttpRequest.

/**
     * Executes the given request asynchronously, handling the response with the given responseHandlerFunction, and
     * returns a {@link CompletableFuture} that represents the result of executing the
     * responseHandlerFunction on the downstream response. Any error anywhere along the way will cause the returned
     * future to be completed with {@link CompletableFuture#completeExceptionally(Throwable)}.
     * <p/>
     * NOTE: This is a helper method for calling {@link #executeAsyncHttpRequest(RequestBuilderWrapper,
     * AsyncResponseHandler, java.util.Deque, java.util.Map)} that uses {@link
     * ChannelAttributes#getHttpProcessingStateForChannel(ChannelHandlerContext)} to extract the {@link
     * HttpProcessingState} from the given ctx argument, and then grabs {@link
     * HttpProcessingState#getDistributedTraceStack()} and {@link HttpProcessingState#getLoggerMdcContextMap()} to use
     * as the distributed trace stack and MDC info for the downstream call.
     */
public <O> CompletableFuture<O> executeAsyncHttpRequest(RequestBuilderWrapper requestBuilderWrapper, AsyncResponseHandler<O> responseHandlerFunction, ChannelHandlerContext ctx) {
    HttpProcessingState state = ChannelAttributes.getHttpProcessingStateForChannel(ctx).get();
    if (state == null)
        throw new IllegalStateException("state cannot be null");
    Map<String, String> mdcContextMap = state.getLoggerMdcContextMap();
    Deque<Span> distributedTraceStack = state.getDistributedTraceStack();
    requestBuilderWrapper.setCtx(ctx);
    return executeAsyncHttpRequest(requestBuilderWrapper, responseHandlerFunction, distributedTraceStack, mdcContextMap);
}
Also used : HttpProcessingState(com.nike.riposte.server.http.HttpProcessingState) Span(com.nike.wingtips.Span)

Aggregations

HttpProcessingState (com.nike.riposte.server.http.HttpProcessingState)54 ChannelHandlerContext (io.netty.channel.ChannelHandlerContext)22 Before (org.junit.Before)20 Channel (io.netty.channel.Channel)19 Attribute (io.netty.util.Attribute)19 RequestInfo (com.nike.riposte.server.http.RequestInfo)14 ResponseInfo (com.nike.riposte.server.http.ResponseInfo)11 Test (org.junit.Test)9 JsonProcessingException (com.fasterxml.jackson.core.JsonProcessingException)8 ErrorResponseBody (com.nike.riposte.server.error.handler.ErrorResponseBody)8 Endpoint (com.nike.riposte.server.http.Endpoint)6 ProxyRouterProcessingState (com.nike.riposte.server.http.ProxyRouterProcessingState)6 Span (com.nike.wingtips.Span)5 ChannelFuture (io.netty.channel.ChannelFuture)5 LastHttpContent (io.netty.handler.codec.http.LastHttpContent)5 LastOutboundMessageSendFullResponseInfo (com.nike.riposte.server.channelpipeline.message.LastOutboundMessageSendFullResponseInfo)4 IncompleteHttpCallTimeoutException (com.nike.riposte.server.error.exception.IncompleteHttpCallTimeoutException)4 TooManyOpenChannelsException (com.nike.riposte.server.error.exception.TooManyOpenChannelsException)4 RequestAndResponseFilter (com.nike.riposte.server.http.filter.RequestAndResponseFilter)4 HttpRequest (io.netty.handler.codec.http.HttpRequest)4