use of org.apache.flink.shaded.netty4.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, true);
} 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);
}
use of org.apache.flink.shaded.netty4.io.netty.handler.codec.http.HttpResponse in project riposte by Nike-Inc.
the class ChannelPipelineFinalizerHandlerTest method doChannelInactive_should_call_accessLogger_if_access_logging_not_already_scheduled.
@DataProvider(value = { "true", "false" }, splitBy = "\\|")
@Test
public void doChannelInactive_should_call_accessLogger_if_access_logging_not_already_scheduled(boolean requestInfoIsNull) throws Exception {
// given
state.setAccessLogCompletedOrScheduled(false);
if (requestInfoIsNull) {
state.setRequestInfo(null);
}
HttpResponse actualResponseMock = mock(HttpResponse.class);
state.setActualResponseObject(actualResponseMock);
HttpProcessingState stateSpy = spy(state);
doReturn(42L).when(stateSpy).calculateTotalRequestTimeMillis();
doReturn(stateSpy).when(stateAttributeMock).get();
Assertions.assertThat(stateSpy.isAccessLogCompletedOrScheduled()).isFalse();
// when
handler.doChannelInactive(ctxMock);
// then
verify(stateSpy).setResponseEndTimeNanosToNowIfNotAlreadySet();
ArgumentCaptor<RequestInfo> requestInfoArgumentCaptor = ArgumentCaptor.forClass(RequestInfo.class);
verify(accessLoggerMock).log(requestInfoArgumentCaptor.capture(), eq(actualResponseMock), eq(responseInfoMock), eq(42L));
RequestInfo actualRequestInfo = requestInfoArgumentCaptor.getValue();
if (requestInfoIsNull) {
Assertions.assertThat(actualRequestInfo.getUri()).isEqualTo(RequestInfo.NONE_OR_UNKNOWN_TAG);
} else {
Assertions.assertThat(actualRequestInfo).isSameAs(requestInfoMock);
}
Assertions.assertThat(stateSpy.isAccessLogCompletedOrScheduled()).isTrue();
}
use of org.apache.flink.shaded.netty4.io.netty.handler.codec.http.HttpResponse in project brave by openzipkin.
the class TracingHttpServerHandler method write.
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise prm) {
Span span = ctx.channel().attr(NettyHttpTracing.SPAN_ATTRIBUTE).get();
if (span == null || !(msg instanceof HttpResponse)) {
ctx.write(msg, prm);
return;
}
HttpResponse response = (HttpResponse) msg;
// Guard re-scoping the same span
SpanInScope spanInScope = ctx.channel().attr(NettyHttpTracing.SPAN_IN_SCOPE_ATTRIBUTE).get();
if (spanInScope == null)
spanInScope = tracer.withSpanInScope(span);
try {
ctx.write(msg, prm);
parser.response(adapter, response, null, span.customizer());
} finally {
// clear scope before reporting
spanInScope.close();
span.finish();
}
}
use of org.apache.flink.shaded.netty4.io.netty.handler.codec.http.HttpResponse in project reactor-netty by reactor.
the class HttpClientOperations method onInboundNext.
@Override
protected void onInboundNext(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof HttpResponse) {
HttpResponse response = (HttpResponse) msg;
if (response.decoderResult().isFailure()) {
onInboundError(response.decoderResult().cause());
return;
}
if (started) {
if (log.isDebugEnabled()) {
log.debug("{} An HttpClientOperations cannot proceed more than one " + "Response", channel(), response.headers().toString());
}
return;
}
started = true;
setNettyResponse(response);
if (!isKeepAlive()) {
markPersistent(false);
}
if (isInboundCancelled()) {
ReferenceCountUtil.release(msg);
return;
}
if (log.isDebugEnabled()) {
log.debug("{} Received response (auto-read:{}) : {}", channel(), channel().config().isAutoRead(), responseHeaders().entries().toString());
}
if (checkResponseCode(response)) {
prefetchMore(ctx);
parentContext().fireContextActive(this);
}
if (msg instanceof FullHttpResponse) {
super.onInboundNext(ctx, msg);
onHandlerTerminate();
}
return;
}
if (msg instanceof LastHttpContent) {
if (!started) {
if (log.isDebugEnabled()) {
log.debug("{} HttpClientOperations received an incorrect end " + "delimiter" + "(previously used connection?)", channel());
}
return;
}
if (log.isDebugEnabled()) {
log.debug("{} Received last HTTP packet", channel());
}
if (msg != LastHttpContent.EMPTY_LAST_CONTENT) {
super.onInboundNext(ctx, msg);
}
// force auto read to enable more accurate close selection now inbound is done
channel().config().setAutoRead(true);
onHandlerTerminate();
return;
}
if (!started) {
if (log.isDebugEnabled()) {
if (msg instanceof ByteBufHolder) {
msg = ((ByteBufHolder) msg).content();
}
log.debug("{} HttpClientOperations received an incorrect chunk " + "" + "(previously used connection?)", channel(), msg);
}
return;
}
super.onInboundNext(ctx, msg);
prefetchMore(ctx);
}
use of org.apache.flink.shaded.netty4.io.netty.handler.codec.http.HttpResponse in project reactor-netty by reactor.
the class HttpServerHandler method channelRead.
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// read message and track if it was keepAlive
if (msg instanceof HttpRequest) {
final HttpRequest request = (HttpRequest) msg;
DecoderResult decoderResult = request.decoderResult();
if (decoderResult.isFailure()) {
Throwable cause = decoderResult.cause();
HttpServerOperations.log.debug("Decoding failed: " + msg + " : ", cause);
HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_0, cause instanceof TooLongFrameException ? HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE : HttpResponseStatus.BAD_REQUEST);
response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, 0).set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
return;
}
if (persistentConnection) {
pendingResponses += 1;
if (HttpServerOperations.log.isDebugEnabled()) {
HttpServerOperations.log.debug("Increasing pending responses, now " + "{}", pendingResponses);
}
persistentConnection = isKeepAlive(request);
} else {
if (HttpServerOperations.log.isDebugEnabled()) {
HttpServerOperations.log.debug("dropping pipelined HTTP request, " + "previous response requested connection close");
}
ReferenceCountUtil.release(msg);
return;
}
if (pendingResponses > 1) {
if (HttpServerOperations.log.isDebugEnabled()) {
HttpServerOperations.log.debug("buffering pipelined HTTP request, " + "pending response count: {}, queue: {}", pendingResponses, pipelined != null ? pipelined.size() : 0);
}
overflow = true;
doPipeline(ctx, msg);
return;
} else {
overflow = false;
parentContext.createOperations(ctx.channel(), msg);
if (!(msg instanceof FullHttpRequest)) {
return;
}
}
} else if (persistentConnection && pendingResponses == 0) {
if (HttpServerOperations.log.isDebugEnabled()) {
HttpServerOperations.log.debug("Dropped HTTP content, " + "Since response has been sent already:{}", msg);
}
if (msg instanceof LastHttpContent) {
ctx.fireChannelRead(msg);
} else {
ReferenceCountUtil.release(msg);
}
ctx.read();
return;
} else if (overflow) {
if (HttpServerOperations.log.isDebugEnabled()) {
HttpServerOperations.log.debug("buffering pipelined HTTP content, " + "pending response count: {}, pending pipeline:{}", pendingResponses, pipelined != null ? pipelined.size() : 0);
}
doPipeline(ctx, msg);
return;
}
ctx.fireChannelRead(msg);
}
Aggregations