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);
}
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;
}
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;
}
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;
}
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);
}
Aggregations