use of io.micronaut.http.netty.channel.ChannelPipelineCustomizer.HANDLER_HTTP2_SETTINGS in project micronaut-core by micronaut-projects.
the class DefaultHttpClient method addFullHttpResponseHandler.
@SuppressWarnings("MagicNumber")
private <O, E> void addFullHttpResponseHandler(io.micronaut.http.HttpRequest<?> request, Channel channel, boolean secure, ChannelPool channelPool, FluxSink<io.micronaut.http.HttpResponse<O>> emitter, Argument<O> bodyType, Argument<E> errorType) {
ChannelPipeline pipeline = channel.pipeline();
final SimpleChannelInboundHandler<FullHttpResponse> newHandler = new SimpleChannelInboundHandlerInstrumented<FullHttpResponse>(false) {
AtomicBoolean complete = new AtomicBoolean(false);
boolean keepAlive = true;
@Override
public boolean acceptInboundMessage(Object msg) {
return msg instanceof FullHttpResponse && (secure || !discardH2cStream((HttpMessage) msg));
}
@Override
protected void channelReadInstrumented(ChannelHandlerContext channelHandlerContext, FullHttpResponse fullResponse) {
try {
HttpResponseStatus status = fullResponse.status();
int statusCode = status.code();
HttpStatus httpStatus;
try {
httpStatus = HttpStatus.valueOf(statusCode);
} catch (IllegalArgumentException e) {
if (complete.compareAndSet(false, true)) {
if (!emitter.isCancelled()) {
emitter.error(e);
}
} else if (log.isWarnEnabled()) {
log.warn("Unsupported http status after handler completed: " + e.getMessage(), e);
}
return;
}
try {
HttpHeaders headers = fullResponse.headers();
if (log.isDebugEnabled()) {
log.debug("Received response {} from {}", status.code(), request.getUri());
}
if (log.isTraceEnabled()) {
traceHeaders(headers);
traceBody("Response", fullResponse.content());
}
// it is a redirect
if (statusCode > 300 && statusCode < 400 && configuration.isFollowRedirects() && headers.contains(HttpHeaderNames.LOCATION)) {
String location = headers.get(HttpHeaderNames.LOCATION);
final MutableHttpRequest<Object> redirectRequest;
if (statusCode == 307) {
redirectRequest = io.micronaut.http.HttpRequest.create(request.getMethod(), location);
request.getBody().ifPresent(redirectRequest::body);
} else {
redirectRequest = io.micronaut.http.HttpRequest.GET(location);
}
setRedirectHeaders(request, redirectRequest);
Flux<io.micronaut.http.HttpResponse<O>> redirectExchange = Flux.from(resolveRedirectURI(request, redirectRequest)).switchMap(buildExchangePublisher(request, redirectRequest, bodyType, errorType));
redirectExchange.defaultIfEmpty(io.micronaut.http.HttpResponse.notFound()).subscribe(oHttpResponse -> {
if (bodyType == null || !bodyType.isVoid()) {
emitter.next(oHttpResponse);
}
emitter.complete();
}, throwable -> {
if (!emitter.isCancelled()) {
emitter.error(throwable);
}
});
return;
}
if (statusCode == HttpStatus.NO_CONTENT.getCode()) {
// normalize the NO_CONTENT header, since http content aggregator adds it even if not present in the response
headers.remove(HttpHeaderNames.CONTENT_LENGTH);
}
boolean convertBodyWithBodyType = statusCode < 400 || (!DefaultHttpClient.this.configuration.isExceptionOnErrorStatus() && bodyType.equalsType(errorType));
FullNettyClientHttpResponse<O> response = new FullNettyClientHttpResponse<>(fullResponse, httpStatus, mediaTypeCodecRegistry, byteBufferFactory, bodyType, convertBodyWithBodyType);
if (complete.compareAndSet(false, true)) {
if (convertBodyWithBodyType) {
if (bodyType == null || !bodyType.isVoid()) {
emitter.next(response);
}
response.onComplete();
emitter.complete();
} else {
// error flow
try {
HttpClientResponseException clientError;
if (errorType != null && errorType != HttpClient.DEFAULT_ERROR_TYPE) {
clientError = new HttpClientResponseException(status.reasonPhrase(), null, response, new HttpClientErrorDecoder() {
@Override
public Argument<?> getErrorType(MediaType mediaType) {
return errorType;
}
});
} else {
clientError = new HttpClientResponseException(status.reasonPhrase(), response);
}
try {
if (!emitter.isCancelled()) {
emitter.error(clientError);
}
} finally {
response.onComplete();
}
} catch (Throwable t) {
if (t instanceof HttpClientResponseException) {
try {
if (!emitter.isCancelled()) {
emitter.error(t);
}
} finally {
response.onComplete();
}
} else {
response.onComplete();
FullNettyClientHttpResponse<Object> errorResponse = new FullNettyClientHttpResponse<>(fullResponse, httpStatus, mediaTypeCodecRegistry, byteBufferFactory, null, false);
errorResponse.onComplete();
HttpClientResponseException clientResponseError = new HttpClientResponseException("Error decoding HTTP error response body: " + t.getMessage(), t, errorResponse, null);
if (!emitter.isCancelled()) {
emitter.error(clientResponseError);
}
}
}
}
}
} catch (Throwable t) {
if (complete.compareAndSet(false, true)) {
if (t instanceof HttpClientResponseException) {
if (!emitter.isCancelled()) {
emitter.error(t);
}
} else {
FullNettyClientHttpResponse<Object> response = new FullNettyClientHttpResponse<>(fullResponse, httpStatus, mediaTypeCodecRegistry, byteBufferFactory, null, false);
HttpClientResponseException clientResponseError = new HttpClientResponseException("Error decoding HTTP response body: " + t.getMessage(), t, response, new HttpClientErrorDecoder() {
@Override
public Argument<?> getErrorType(MediaType mediaType) {
return errorType;
}
});
try {
if (!emitter.isCancelled()) {
emitter.error(clientResponseError);
}
} finally {
response.onComplete();
}
}
} else {
if (log.isWarnEnabled()) {
log.warn("Exception fired after handler completed: " + t.getMessage(), t);
}
}
}
} finally {
if (fullResponse.refCnt() > 0) {
try {
ReferenceCountUtil.release(fullResponse);
} catch (Throwable e) {
if (log.isDebugEnabled()) {
log.debug("Failed to release response: {}", fullResponse);
}
}
}
if (!HttpUtil.isKeepAlive(fullResponse)) {
keepAlive = false;
}
pipeline.remove(this);
}
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) {
if (channelPool != null) {
if (readTimeoutMillis != null) {
ctx.pipeline().remove(ChannelPipelineCustomizer.HANDLER_READ_TIMEOUT);
}
final Channel ch = ctx.channel();
if (!keepAlive) {
ch.closeFuture().addListener((future -> channelPool.release(ch)));
} else {
channelPool.release(ch);
}
} else {
// just close it to prevent any future reads without a handler registered
ctx.close();
}
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) {
if (readTimeoutMillis != null) {
if (httpVersion == io.micronaut.http.HttpVersion.HTTP_2_0) {
Http2SettingsHandler settingsHandler = (Http2SettingsHandler) ctx.pipeline().get(HANDLER_HTTP2_SETTINGS);
if (settingsHandler != null) {
addInstrumentedListener(settingsHandler.promise, future -> {
if (future.isSuccess()) {
pipeline.addBefore(ChannelPipelineCustomizer.HANDLER_HTTP2_CONNECTION, ChannelPipelineCustomizer.HANDLER_READ_TIMEOUT, new ReadTimeoutHandler(readTimeoutMillis, TimeUnit.MILLISECONDS));
}
});
} else {
pipeline.addBefore(ChannelPipelineCustomizer.HANDLER_HTTP2_CONNECTION, ChannelPipelineCustomizer.HANDLER_READ_TIMEOUT, new ReadTimeoutHandler(readTimeoutMillis, TimeUnit.MILLISECONDS));
}
} else {
pipeline.addBefore(ChannelPipelineCustomizer.HANDLER_HTTP_CLIENT_CODEC, ChannelPipelineCustomizer.HANDLER_READ_TIMEOUT, new ReadTimeoutHandler(readTimeoutMillis, TimeUnit.MILLISECONDS));
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
try {
if (complete.compareAndSet(false, true)) {
String message = cause.getMessage();
if (message == null) {
message = cause.getClass().getSimpleName();
}
if (log.isTraceEnabled()) {
log.trace("HTTP Client exception ({}) occurred for request : {} {}", message, request.getMethodName(), request.getUri());
}
if (cause instanceof TooLongFrameException) {
if (!emitter.isCancelled()) {
emitter.error(new ContentLengthExceededException(configuration.getMaxContentLength()));
}
} else if (cause instanceof io.netty.handler.timeout.ReadTimeoutException) {
if (!emitter.isCancelled()) {
emitter.error(ReadTimeoutException.TIMEOUT_EXCEPTION);
}
} else {
if (!emitter.isCancelled()) {
emitter.error(new HttpClientException("Error occurred reading HTTP response: " + message, cause));
}
}
}
} finally {
keepAlive = false;
pipeline.remove(this);
}
}
};
pipeline.addLast(ChannelPipelineCustomizer.HANDLER_MICRONAUT_FULL_HTTP_RESPONSE, newHandler);
}
Aggregations