use of io.micronaut.http.netty.stream.StreamedHttpResponse in project micronaut-core by micronaut-projects.
the class DefaultHttpClient method buildExchangeStreamPublisher.
/**
* @param request The request
* @param errorType The error type
* @param <I> The input type
* @return A {@link Function}
*/
protected <I> Function<URI, Publisher<HttpResponse<ByteBuffer<?>>>> buildExchangeStreamPublisher(@NonNull io.micronaut.http.HttpRequest<I> request, @NonNull Argument<?> errorType) {
final io.micronaut.http.HttpRequest<Object> parentRequest = ServerRequestContext.currentRequest().orElse(null);
return requestURI -> {
Flux<io.micronaut.http.HttpResponse<Object>> streamResponsePublisher = Flux.from(buildStreamExchange(parentRequest, request, requestURI, errorType));
return streamResponsePublisher.switchMap(response -> {
StreamedHttpResponse streamedHttpResponse = NettyHttpResponseBuilder.toStreamResponse(response);
Flux<HttpContent> httpContentReactiveSequence = Flux.from(streamedHttpResponse);
return httpContentReactiveSequence.filter(message -> !(message.content() instanceof EmptyByteBuf)).map(message -> {
ByteBuf byteBuf = message.content();
if (log.isTraceEnabled()) {
log.trace("HTTP Client Streaming Response Received Chunk (length: {}) for Request: {} {}", byteBuf.readableBytes(), request.getMethodName(), request.getUri());
traceBody("Response", byteBuf);
}
ByteBuffer<?> byteBuffer = byteBufferFactory.wrap(byteBuf);
NettyStreamedHttpResponse<ByteBuffer<?>> thisResponse = new NettyStreamedHttpResponse<>(streamedHttpResponse, response.status());
thisResponse.setBody(byteBuffer);
return (HttpResponse<ByteBuffer<?>>) new HttpResponseWrapper<>(thisResponse);
});
}).doOnTerminate(() -> {
final Object o = request.getAttribute(NettyClientHttpRequest.CHANNEL).orElse(null);
if (o instanceof Channel) {
final Channel c = (Channel) o;
if (c.isOpen()) {
c.close();
}
}
});
};
}
use of io.micronaut.http.netty.stream.StreamedHttpResponse in project micronaut-core by micronaut-projects.
the class DefaultHttpClient method readBodyOnError.
private Flux<io.micronaut.http.HttpResponse<Object>> readBodyOnError(@NonNull Argument<?> errorType, @NonNull Flux<io.micronaut.http.HttpResponse<Object>> publisher) {
if (errorType != null && errorType != HttpClient.DEFAULT_ERROR_TYPE) {
return publisher.onErrorResume(clientException -> {
if (clientException instanceof HttpClientResponseException) {
final HttpResponse<?> response = ((HttpClientResponseException) clientException).getResponse();
if (response instanceof NettyStreamedHttpResponse) {
return Mono.create(emitter -> {
NettyStreamedHttpResponse<?> streamedResponse = (NettyStreamedHttpResponse<?>) response;
final StreamedHttpResponse nettyResponse = streamedResponse.getNettyResponse();
nettyResponse.subscribe(new Subscriber<HttpContent>() {
final CompositeByteBuf buffer = byteBufferFactory.getNativeAllocator().compositeBuffer();
Subscription s;
@Override
public void onSubscribe(Subscription s) {
this.s = s;
s.request(1);
}
@Override
public void onNext(HttpContent httpContent) {
buffer.addComponent(true, httpContent.content());
s.request(1);
}
@Override
public void onError(Throwable t) {
buffer.release();
emitter.error(t);
}
@Override
public void onComplete() {
try {
FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(nettyResponse.protocolVersion(), nettyResponse.status(), buffer, nettyResponse.headers(), new DefaultHttpHeaders(true));
final FullNettyClientHttpResponse<Object> fullNettyClientHttpResponse = new FullNettyClientHttpResponse<>(fullHttpResponse, response.status(), mediaTypeCodecRegistry, byteBufferFactory, (Argument<Object>) errorType, true);
fullNettyClientHttpResponse.onComplete();
emitter.error(new HttpClientResponseException(fullHttpResponse.status().reasonPhrase(), null, fullNettyClientHttpResponse, new HttpClientErrorDecoder() {
@Override
public Argument<?> getErrorType(MediaType mediaType) {
return errorType;
}
}));
} finally {
buffer.release();
}
}
});
});
}
}
return Mono.error(clientException);
});
}
return publisher;
}
use of io.micronaut.http.netty.stream.StreamedHttpResponse in project micronaut-core by micronaut-projects.
the class DefaultHttpClient method streamRequestThroughChannel.
private void streamRequestThroughChannel(io.micronaut.http.HttpRequest<?> parentRequest, AtomicReference<io.micronaut.http.HttpRequest> requestWrapper, FluxSink emitter, Channel channel, boolean failOnError) throws HttpPostRequestEncoder.ErrorDataEncoderException {
io.micronaut.http.HttpRequest<?> finalRequest = requestWrapper.get();
URI requestURI = finalRequest.getUri();
NettyRequestWriter requestWriter = prepareRequest(finalRequest, requestURI, emitter, false);
HttpRequest nettyRequest = requestWriter.getNettyRequest();
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(ChannelPipelineCustomizer.HANDLER_MICRONAUT_HTTP_RESPONSE_FULL, new SimpleChannelInboundHandlerInstrumented<FullHttpResponse>() {
final AtomicBoolean received = new AtomicBoolean(false);
@Override
public boolean acceptInboundMessage(Object msg) {
return msg instanceof FullHttpResponse;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
if (received.compareAndSet(false, true)) {
emitter.error(cause);
}
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent && received.compareAndSet(false, true)) {
// closed to idle ste
emitter.error(ReadTimeoutException.TIMEOUT_EXCEPTION);
}
}
@Override
protected void channelReadInstrumented(ChannelHandlerContext ctx, FullHttpResponse msg) {
if (received.compareAndSet(false, true)) {
HttpResponseStatus status = msg.status();
int statusCode = status.code();
HttpStatus httpStatus;
try {
httpStatus = HttpStatus.valueOf(statusCode);
} catch (IllegalArgumentException e) {
emitter.error(e);
return;
}
Publisher<HttpContent> bodyPublisher;
if (msg.content() instanceof EmptyByteBuf) {
bodyPublisher = Publishers.empty();
} else {
bodyPublisher = Publishers.just(new DefaultLastHttpContent(msg.content()));
}
DefaultStreamedHttpResponse nettyResponse = new DefaultStreamedHttpResponse(msg.protocolVersion(), msg.status(), msg.headers(), bodyPublisher);
NettyStreamedHttpResponse response = new NettyStreamedHttpResponse(nettyResponse, httpStatus);
HttpHeaders headers = msg.headers();
if (log.isTraceEnabled()) {
log.trace("HTTP Client Streaming Response Received ({}) for Request: {} {}", msg.status(), nettyRequest.method().name(), nettyRequest.uri());
traceHeaders(headers);
}
boolean errorStatus = statusCode >= 400;
if (errorStatus && failOnError) {
emitter.error(new HttpClientResponseException(response.getStatus().getReason(), response));
} else {
emitter.next(response);
emitter.complete();
}
}
}
});
pipeline.addLast(ChannelPipelineCustomizer.HANDLER_MICRONAUT_HTTP_RESPONSE_STREAM, new SimpleChannelInboundHandlerInstrumented<StreamedHttpResponse>() {
final AtomicBoolean received = new AtomicBoolean(false);
@Override
public boolean acceptInboundMessage(Object msg) {
return msg instanceof StreamedHttpResponse;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
if (received.compareAndSet(false, true)) {
emitter.error(cause);
}
}
@Override
protected void channelReadInstrumented(ChannelHandlerContext ctx, StreamedHttpResponse msg) {
if (received.compareAndSet(false, true)) {
HttpResponseStatus status = msg.status();
int statusCode = status.code();
HttpStatus httpStatus;
try {
httpStatus = HttpStatus.valueOf(statusCode);
} catch (IllegalArgumentException e) {
emitter.error(e);
return;
}
NettyStreamedHttpResponse response = new NettyStreamedHttpResponse(msg, httpStatus);
HttpHeaders headers = msg.headers();
if (log.isTraceEnabled()) {
log.trace("HTTP Client Streaming Response Received ({}) for Request: {} {}", msg.status(), nettyRequest.method().name(), nettyRequest.uri());
traceHeaders(headers);
}
if (statusCode > 300 && statusCode < 400 && configuration.isFollowRedirects() && headers.contains(HttpHeaderNames.LOCATION)) {
String location = headers.get(HttpHeaderNames.LOCATION);
Flux<io.micronaut.http.HttpResponse<Object>> redirectedExchange;
try {
MutableHttpRequest<Object> redirectRequest;
if (statusCode == 307) {
redirectRequest = io.micronaut.http.HttpRequest.create(finalRequest.getMethod(), location);
finalRequest.getBody().ifPresent(redirectRequest::body);
} else {
redirectRequest = io.micronaut.http.HttpRequest.GET(location);
}
setRedirectHeaders(nettyRequest, redirectRequest);
redirectedExchange = Flux.from(resolveRedirectURI(parentRequest, redirectRequest)).flatMap(uri -> buildStreamExchange(parentRequest, redirectRequest, uri, null));
// noinspection SubscriberImplementation
redirectedExchange.subscribe(new Subscriber<io.micronaut.http.HttpResponse<Object>>() {
Subscription sub;
@Override
public void onSubscribe(Subscription s) {
s.request(1);
this.sub = s;
}
@Override
public void onNext(io.micronaut.http.HttpResponse<Object> objectHttpResponse) {
emitter.next(objectHttpResponse);
}
@Override
public void onError(Throwable t) {
emitter.error(t);
}
@Override
public void onComplete() {
emitter.complete();
}
});
} catch (Exception e) {
emitter.error(e);
}
} else {
boolean errorStatus = statusCode >= 400;
if (errorStatus && failOnError) {
emitter.error(new HttpClientResponseException(response.getStatus().getReason(), response));
} else {
emitter.next(response);
emitter.complete();
}
}
}
}
});
if (log.isDebugEnabled()) {
debugRequest(requestURI, nettyRequest);
}
if (log.isTraceEnabled()) {
traceRequest(requestWrapper.get(), nettyRequest);
}
requestWriter.writeAndClose(channel, null, emitter);
}
use of io.micronaut.http.netty.stream.StreamedHttpResponse in project micronaut-core by micronaut-projects.
the class NettyHttpResponseBuilder method toStreamResponse.
/**
* Convert the given response to a full http response.
* @param response The response
* @return The full response.
*/
@NonNull
static StreamedHttpResponse toStreamResponse(@NonNull io.micronaut.http.HttpResponse<?> response) {
Objects.requireNonNull(response, "The response cannot be null");
while (response instanceof HttpResponseWrapper) {
response = ((HttpResponseWrapper<?>) response).getDelegate();
}
if (response instanceof NettyHttpResponseBuilder) {
NettyHttpResponseBuilder builder = (NettyHttpResponseBuilder) response;
if (builder.isStream()) {
return builder.toStreamHttpResponse();
} else {
FullHttpResponse fullHttpResponse = builder.toFullHttpResponse();
return new DefaultStreamedHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(response.code(), response.reason()), Publishers.just(new DefaultLastHttpContent(fullHttpResponse.content())));
}
}
// manual conversion
StreamedHttpResponse fullHttpResponse;
ByteBuf byteBuf = response.getBody(ByteBuf.class).orElse(null);
if (byteBuf != null) {
fullHttpResponse = new DefaultStreamedHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(response.code(), response.reason()), Publishers.just(new DefaultLastHttpContent(byteBuf)));
} else {
fullHttpResponse = new DefaultStreamedHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(response.code(), response.reason()), Publishers.empty());
}
response.getHeaders().forEach((s, strings) -> fullHttpResponse.headers().add(s, strings));
return fullHttpResponse;
}
use of io.micronaut.http.netty.stream.StreamedHttpResponse in project micronaut-core by micronaut-projects.
the class DefaultHttpClient method buildJsonStreamPublisher.
/**
* @param parentRequest The parent request
* @param request The request
* @param type The type
* @param errorType The error type
* @param <I> The input type
* @param <O> The output type
* @return A {@link Function}
*/
protected <I, O> Function<URI, Publisher<O>> buildJsonStreamPublisher(io.micronaut.http.HttpRequest<?> parentRequest, io.micronaut.http.HttpRequest<I> request, io.micronaut.core.type.Argument<O> type, io.micronaut.core.type.Argument<?> errorType) {
return requestURI -> {
Flux<io.micronaut.http.HttpResponse<Object>> streamResponsePublisher = Flux.from(buildStreamExchange(parentRequest, request, requestURI, errorType));
return streamResponsePublisher.switchMap(response -> {
if (!(response instanceof NettyStreamedHttpResponse)) {
throw new IllegalStateException("Response has been wrapped in non streaming type. Do not wrap the response in client filters for stream requests");
}
MapperMediaTypeCodec mediaTypeCodec = (MapperMediaTypeCodec) mediaTypeCodecRegistry.findCodec(MediaType.APPLICATION_JSON_TYPE).orElseThrow(() -> new IllegalStateException("No JSON codec found"));
StreamedHttpResponse streamResponse = NettyHttpResponseBuilder.toStreamResponse(response);
Flux<HttpContent> httpContentReactiveSequence = Flux.from(streamResponse);
boolean isJsonStream = response.getContentType().map(mediaType -> mediaType.equals(MediaType.APPLICATION_JSON_STREAM_TYPE)).orElse(false);
boolean streamArray = !Iterable.class.isAssignableFrom(type.getType()) && !isJsonStream;
Processor<byte[], JsonNode> jsonProcessor = mediaTypeCodec.getJsonMapper().createReactiveParser(p -> {
httpContentReactiveSequence.map(content -> {
ByteBuf chunk = content.content();
if (log.isTraceEnabled()) {
log.trace("HTTP Client Streaming Response Received Chunk (length: {}) for Request: {} {}", chunk.readableBytes(), request.getMethodName(), request.getUri());
traceBody("Chunk", chunk);
}
try {
return ByteBufUtil.getBytes(chunk);
} finally {
chunk.release();
}
}).subscribe(p);
}, streamArray);
return Flux.from(jsonProcessor).map(jsonNode -> mediaTypeCodec.decode(type, jsonNode));
}).doOnTerminate(() -> {
final Object o = request.getAttribute(NettyClientHttpRequest.CHANNEL).orElse(null);
if (o instanceof Channel) {
final Channel c = (Channel) o;
if (c.isOpen()) {
c.close();
}
}
});
};
}
Aggregations