use of io.netty.handler.codec.http.HttpObject in project jocean-http by isdom.
the class RxNettys method httpObjectsAsBytes.
public static byte[] httpObjectsAsBytes(final Iterator<HttpObject> itr) throws IOException {
final CompositeByteBuf composite = Unpooled.compositeBuffer();
try {
while (itr.hasNext()) {
final HttpObject obj = itr.next();
if (obj instanceof HttpContent) {
composite.addComponent(((HttpContent) obj).content());
}
}
composite.setIndex(0, composite.capacity());
@SuppressWarnings("resource") final InputStream is = new ByteBufInputStream(composite);
final byte[] bytes = new byte[is.available()];
is.read(bytes);
return bytes;
} finally {
ReferenceCountUtil.release(composite);
}
}
use of io.netty.handler.codec.http.HttpObject in project jocean-http by isdom.
the class MessageUtil method interact.
public static Interact interact(final HttpClient client) {
final InitiatorBuilder _initiatorBuilder = client.initiator();
final AtomicBoolean _isSSLEnabled = new AtomicBoolean(false);
final AtomicReference<Observable<Object>> _obsreqRef = new AtomicReference<>(fullRequestWithoutBody(HttpVersion.HTTP_1_1, HttpMethod.GET));
final List<String> _nvs = new ArrayList<>();
final AtomicReference<URI> _uriRef = new AtomicReference<>();
return new Interact() {
private void updateObsRequest(final Action1<Object> action) {
_obsreqRef.set(_obsreqRef.get().doOnNext(action));
}
private void addQueryParams() {
if (!_nvs.isEmpty()) {
updateObsRequest(MessageUtil.addQueryParam(_nvs.toArray(new String[0])));
}
}
private void extractUriWithHost(final Object... reqbeans) {
if (null == _uriRef.get()) {
for (Object bean : reqbeans) {
try {
final Path path = bean.getClass().getAnnotation(Path.class);
if (null != path) {
final URI uri = new URI(path.value());
if (null != uri.getHost()) {
uri(path.value());
return;
}
}
} catch (Exception e) {
LOG.warn("exception when extract uri from bean {}, detail: {}", bean, ExceptionUtils.exception2detail(e));
}
}
}
}
private void checkAddr() {
if (null == _uriRef.get()) {
throw new RuntimeException("remote address not set.");
}
}
private InitiatorBuilder addSSLFeatureIfNeed(final InitiatorBuilder builder) {
if (_isSSLEnabled.get()) {
return builder;
} else if ("https".equals(_uriRef.get().getScheme())) {
return builder.feature(F_SSL);
} else {
return builder;
}
}
@Override
public Interact method(final HttpMethod method) {
updateObsRequest(MessageUtil.setMethod(method));
return this;
}
@Override
public Interact uri(final String uriAsString) {
try {
final URI uri = new URI(uriAsString);
_uriRef.set(uri);
_initiatorBuilder.remoteAddress(uri2addr(uri));
updateObsRequest(MessageUtil.setHost(uri));
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
return this;
}
@Override
public Interact path(final String path) {
updateObsRequest(MessageUtil.setPath(path));
return this;
}
@Override
public Interact paramAsQuery(final String name, final String value) {
_nvs.add(name);
_nvs.add(value);
return this;
}
@Override
public Interact reqbean(final Object... reqbeans) {
updateObsRequest(MessageUtil.toRequest(reqbeans));
extractUriWithHost(reqbeans);
return this;
}
@Override
public Interact body(final Observable<? extends MessageBody> body) {
_obsreqRef.set(_obsreqRef.get().compose(MessageUtil.addBody(body)));
return this;
}
@Override
public Interact body(final Object bean, final ContentEncoder contentEncoder) {
return body(toBody(bean, contentEncoder.contentType(), contentEncoder.encoder()));
}
// @Override
// public Interact disposeBodyOnTerminate(final boolean doDispose) {
// _doDisposeBody.set(doDispose);
// return this;
// }
@Override
public Interact onrequest(final Action1<Object> action) {
updateObsRequest(action);
return this;
}
@Override
public Interact feature(final Feature... features) {
_initiatorBuilder.feature(features);
if (isSSLEnabled(features)) {
_isSSLEnabled.set(true);
}
return this;
}
private boolean isSSLEnabled(final Feature... features) {
for (Feature f : features) {
if (f instanceof Feature.ENABLE_SSL) {
return true;
}
}
return false;
}
private Observable<? extends Object> hookDisposeBody(final Observable<Object> obsreq, final HttpInitiator initiator) {
return obsreq.doOnNext(DisposableWrapperUtil.disposeOnForAny(initiator));
}
private Observable<? extends DisposableWrapper<HttpObject>> defineInteraction(final HttpInitiator initiator) {
return initiator.defineInteraction(hookDisposeBody(_obsreqRef.get(), initiator));
}
@Override
public Observable<? extends Interaction> execution() {
checkAddr();
addQueryParams();
return addSSLFeatureIfNeed(_initiatorBuilder).build().map(new Func1<HttpInitiator, Interaction>() {
@Override
public Interaction call(final HttpInitiator initiator) {
final Observable<? extends DisposableWrapper<HttpObject>> interaction = defineInteraction(initiator);
return new Interaction() {
@Override
public HttpInitiator initiator() {
return initiator;
}
@Override
public Observable<? extends DisposableWrapper<HttpObject>> execute() {
return interaction;
}
};
}
});
}
};
}
use of io.netty.handler.codec.http.HttpObject in project vert.x by eclipse.
the class Http1xServerResponse method write.
private Http1xServerResponse write(ByteBuf chunk, PromiseInternal<Void> promise) {
synchronized (conn) {
if (written) {
throw new IllegalStateException("Response has already been written");
} else if (!headWritten && !headers.contains(HttpHeaders.TRANSFER_ENCODING) && !headers.contains(HttpHeaders.CONTENT_LENGTH)) {
if (version != HttpVersion.HTTP_1_0) {
throw new IllegalStateException("You must set the Content-Length header to be the total size of the message " + "body BEFORE sending any data if you are not using HTTP chunked encoding.");
}
}
bytesWritten += chunk.readableBytes();
HttpObject msg;
if (!headWritten) {
prepareHeaders(-1);
msg = new AssembledHttpResponse(head, version, status, headers, chunk);
} else {
msg = new DefaultHttpContent(chunk);
}
conn.writeToChannel(msg, promise);
return this;
}
}
use of io.netty.handler.codec.http.HttpObject in project riposte by Nike-Inc.
the class StreamingAsyncHttpClient method prepChannelForDownstreamCall.
protected void prepChannelForDownstreamCall(String downstreamHost, int downstreamPort, 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, ProxyRouterProcessingState proxyRouterProcessingState, @Nullable Span spanForDownstreamCall) throws SSLException, NoSuchAlgorithmException, KeyStoreException {
ChannelHandler chunkSenderHandler = new SimpleChannelInboundHandler<HttpObject>() {
@Override
protected void channelRead0(ChannelHandlerContext downstreamCallCtx, HttpObject msg) {
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(() -> {
Span currentSpan = Tracer.getInstance().getCurrentSpan();
if (proxyRouterProcessingState != null) {
proxyRouterProcessingState.handleTracingResponseTaggingAndFinalSpanNameIfNotAlreadyDone(currentSpan);
}
if (proxySpanTaggingStrategy.shouldAddWireReceiveFinishAnnotation()) {
currentSpan.addTimestampedAnnotationForCurrentTime(proxySpanTaggingStrategy.wireReceiveFinishAnnotationName());
}
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.protocolVersion(), origHttpResponse.status(), ((FullHttpResponse) msg).content()) : new DefaultHttpResponse(origHttpResponse.protocolVersion(), origHttpResponse.status());
httpResponse.headers().add(origHttpResponse.headers());
msgToPass = httpResponse;
if (proxyRouterProcessingState != null) {
proxyRouterProcessingState.setProxyHttpResponse(httpResponse);
}
if (spanForDownstreamCall != null && proxySpanTaggingStrategy.shouldAddWireReceiveStartAnnotation()) {
spanForDownstreamCall.addTimestampedAnnotationForCurrentTime(proxySpanTaggingStrategy.wireReceiveStartAnnotationName());
}
}
callback.messageReceived(msgToPass);
} else {
if (shouldLogBadMessagesAfterRequestFinishes) {
runnableWithTracingAndMdc(() -> logger.warn("Received HttpObject msg when call was not active: {}", 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 (proxyRouterProcessingState != null) {
proxyRouterProcessingState.setProxyError(cause);
}
if (performSubSpanAroundDownstreamCalls) {
Span currentSpan = Tracer.getInstance().getCurrentSpan();
HttpResponse proxyHttpResponseObj = (proxyRouterProcessingState == null) ? null : proxyRouterProcessingState.getProxyHttpResponse();
if (currentSpan != null && proxySpanTaggingStrategy.shouldAddErrorAnnotationForCaughtException(proxyHttpResponseObj, cause)) {
currentSpan.addTimestampedAnnotationForCurrentTime(proxySpanTaggingStrategy.errorAnnotationName(proxyHttpResponseObj, cause));
}
if (proxyRouterProcessingState != null) {
proxyRouterProcessingState.handleTracingResponseTaggingAndFinalSpanNameIfNotAlreadyDone(currentSpan);
}
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) {
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) {
// Check and see if there's already an existing SslHandler in the pipeline. If it's pointed at the same
// host/port we need for this call, then we can leave it alone and don't need to create a new one.
boolean requiresNewSslHandler = true;
if (registeredHandlerNames.contains(SSL_HANDLER_NAME)) {
SslHandler existingSslHandler = (SslHandler) p.get(SSL_HANDLER_NAME);
SSLEngine existingSslEngine = existingSslHandler.engine();
if (Objects.equals(downstreamHost, existingSslEngine.getPeerHost()) && Objects.equals(downstreamPort, existingSslEngine.getPeerPort())) {
// The existing SslHandler's SslEngine is pointed at the correct host/port. We don't need to
// add a new one.
requiresNewSslHandler = false;
} else {
// The existing SslHandler's SslEngine is *not* pointed at the correct host/port. We need a new one,
// so remove the old one.
p.remove(SSL_HANDLER_NAME);
}
}
if (requiresNewSslHandler) {
// SSL call and we need to add a SslHandler. Create the general-purpose reusable SslContexts if needed.
if (clientSslCtx == null) {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore) null);
clientSslCtx = SslContextBuilder.forClient().trustManager(tmf).build();
}
if (insecureSslCtx == null) {
insecureSslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
}
// Figure out which SslContext to use for this call.
SslContext sslCtxToUse = (relaxedHttpsValidation) ? insecureSslCtx : clientSslCtx;
// Create the SslHandler and configure the SslEngine
// as per the javadocs for SslContext.newHandler(ByteBufAllocator, String, int).
SslHandler sslHandler = sslCtxToUse.newHandler(ch.alloc(), downstreamHost, downstreamPort);
SSLEngine sslEngine = sslHandler.engine();
SSLParameters sslParameters = sslEngine.getSSLParameters();
sslParameters.setEndpointIdentificationAlgorithm(HTTPS);
sslEngine.setSSLParameters(sslParameters);
// Add the SslHandler to the pipeline in the correct location.
p.addAfter(DOWNSTREAM_CALL_TIMEOUT_HANDLER_NAME, SSL_HANDLER_NAME, sslHandler);
}
} 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 io.netty.handler.codec.http.HttpObject in project riposte by Nike-Inc.
the class ComponentTestUtils method createNettyHttpClientBootstrap.
public static Bootstrap createNettyHttpClientBootstrap() {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(new NioEventLoopGroup()).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new HttpClientCodec());
p.addLast(new HttpObjectAggregator(Integer.MAX_VALUE));
p.addLast("clientResponseHandler", new SimpleChannelInboundHandler<HttpObject>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
throw new RuntimeException("Client response handler was not setup before the call");
}
});
}
});
return bootstrap;
}
Aggregations