use of io.micronaut.http.annotation.Body in project micronaut-core by micronaut-projects.
the class RoutingInBoundHandler method writeFinalNettyResponse.
private void writeFinalNettyResponse(MutableHttpResponse<?> message, HttpRequest<?> request, ChannelHandlerContext context) {
HttpStatus httpStatus = message.status();
final io.micronaut.http.HttpVersion httpVersion = request.getHttpVersion();
final boolean isHttp2 = httpVersion == io.micronaut.http.HttpVersion.HTTP_2_0;
boolean decodeError = request instanceof NettyHttpRequest && ((NettyHttpRequest<?>) request).getNativeRequest().decoderResult().isFailure();
final Object body = message.body();
if (body instanceof NettyCustomizableResponseTypeHandlerInvoker) {
// default Connection header if not set explicitly
if (!isHttp2) {
if (!message.getHeaders().contains(HttpHeaders.CONNECTION)) {
if (!decodeError && (httpStatus.getCode() < 500 || serverConfiguration.isKeepAliveOnServerError())) {
message.getHeaders().set(HttpHeaders.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
} else {
message.getHeaders().set(HttpHeaders.CONNECTION, HttpHeaderValues.CLOSE);
}
}
}
NettyCustomizableResponseTypeHandlerInvoker handler = (NettyCustomizableResponseTypeHandlerInvoker) body;
message.body(null);
handler.invoke(request, message, context);
} else {
io.netty.handler.codec.http.HttpResponse nettyResponse = NettyHttpResponseBuilder.toHttpResponse(message);
io.netty.handler.codec.http.HttpHeaders nettyHeaders = nettyResponse.headers();
// default Connection header if not set explicitly
if (!isHttp2) {
if (!nettyHeaders.contains(HttpHeaderNames.CONNECTION)) {
boolean expectKeepAlive = nettyResponse.protocolVersion().isKeepAliveDefault() || request.getHeaders().isKeepAlive();
if (!decodeError && (expectKeepAlive || httpStatus.getCode() < 500 || serverConfiguration.isKeepAliveOnServerError())) {
nettyHeaders.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
} else {
nettyHeaders.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
}
}
}
// default to Transfer-Encoding: chunked if Content-Length not set or not already set
if (!nettyHeaders.contains(HttpHeaderNames.CONTENT_LENGTH) && !nettyHeaders.contains(HttpHeaderNames.TRANSFER_ENCODING)) {
nettyHeaders.set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED);
}
// close handled by HttpServerKeepAliveHandler
final NettyHttpRequest<?> nettyHttpRequest = (NettyHttpRequest<?>) request;
if (isHttp2) {
addHttp2StreamHeader(request, nettyResponse);
}
io.netty.handler.codec.http.HttpRequest nativeRequest = nettyHttpRequest.getNativeRequest();
GenericFutureListener<Future<? super Void>> requestCompletor = future -> {
try {
if (!future.isSuccess()) {
final Throwable throwable = future.cause();
if (!(throwable instanceof ClosedChannelException)) {
if (throwable instanceof Http2Exception.StreamException) {
Http2Exception.StreamException se = (Http2Exception.StreamException) throwable;
if (se.error() == Http2Error.STREAM_CLOSED) {
// ignore
return;
}
}
if (LOG.isErrorEnabled()) {
LOG.error("Error writing final response: " + throwable.getMessage(), throwable);
}
}
}
} finally {
cleanupRequest(context, nettyHttpRequest);
context.read();
}
};
if (nativeRequest instanceof StreamedHttpRequest && !((StreamedHttpRequest) nativeRequest).isConsumed()) {
StreamedHttpRequest streamedHttpRequest = (StreamedHttpRequest) nativeRequest;
// We have to clear the buffer of FlowControlHandler before writing the response
// If this is a streamed request and there is still content to consume then subscribe
// and write the buffer is empty.
// noinspection ReactiveStreamsSubscriberImplementation
streamedHttpRequest.subscribe(new Subscriber<HttpContent>() {
private Subscription streamSub;
@Override
public void onSubscribe(Subscription s) {
streamSub = s;
s.request(1);
}
@Override
public void onNext(HttpContent httpContent) {
httpContent.release();
streamSub.request(1);
}
@Override
public void onError(Throwable t) {
syncWriteAndFlushNettyResponse(context, request, nettyResponse, requestCompletor);
}
@Override
public void onComplete() {
syncWriteAndFlushNettyResponse(context, request, nettyResponse, requestCompletor);
}
});
} else {
syncWriteAndFlushNettyResponse(context, request, nettyResponse, requestCompletor);
}
}
}
use of io.micronaut.http.annotation.Body in project micronaut-core by micronaut-projects.
the class RoutingInBoundHandler method mapToHttpContent.
private Flux<HttpContent> mapToHttpContent(NettyHttpRequest<?> request, MutableHttpResponse<?> response, Object body, ChannelHandlerContext context) {
final RouteInfo<?> routeInfo = response.getAttribute(HttpAttributes.ROUTE_INFO, RouteInfo.class).orElse(null);
final boolean hasRouteInfo = routeInfo != null;
MediaType mediaType = response.getContentType().orElse(null);
if (mediaType == null && hasRouteInfo) {
mediaType = routeExecutor.resolveDefaultResponseContentType(request, routeInfo);
}
boolean isJson = mediaType != null && mediaType.getExtension().equals(MediaType.EXTENSION_JSON) && isJsonFormattable(hasRouteInfo ? routeInfo.getBodyType() : null);
NettyByteBufferFactory byteBufferFactory = new NettyByteBufferFactory(context.alloc());
Flux<Object> bodyPublisher = Flux.from(Publishers.convertPublisher(body, Publisher.class));
MediaType finalMediaType = mediaType;
Flux<HttpContent> httpContentPublisher = bodyPublisher.map(message -> {
HttpContent httpContent;
if (message instanceof ByteBuf) {
httpContent = new DefaultHttpContent((ByteBuf) message);
} else if (message instanceof ByteBuffer) {
ByteBuffer<?> byteBuffer = (ByteBuffer<?>) message;
Object nativeBuffer = byteBuffer.asNativeBuffer();
if (nativeBuffer instanceof ByteBuf) {
httpContent = new DefaultHttpContent((ByteBuf) nativeBuffer);
} else {
httpContent = new DefaultHttpContent(Unpooled.copiedBuffer(byteBuffer.asNioBuffer()));
}
} else if (message instanceof byte[]) {
httpContent = new DefaultHttpContent(Unpooled.copiedBuffer((byte[]) message));
} else if (message instanceof HttpContent) {
httpContent = (HttpContent) message;
} else {
MediaTypeCodec codec = mediaTypeCodecRegistry.findCodec(finalMediaType, message.getClass()).orElse(new TextPlainCodec(serverConfiguration.getDefaultCharset()));
if (LOG.isTraceEnabled()) {
LOG.trace("Encoding emitted response object [{}] using codec: {}", message, codec);
}
ByteBuffer<ByteBuf> encoded;
if (hasRouteInfo) {
// noinspection unchecked
final Argument<Object> bodyType = (Argument<Object>) routeInfo.getBodyType();
if (bodyType.isInstance(message)) {
encoded = codec.encode(bodyType, message, byteBufferFactory);
} else {
encoded = codec.encode(message, byteBufferFactory);
}
} else {
encoded = codec.encode(message, byteBufferFactory);
}
httpContent = new DefaultHttpContent(encoded.asNativeBuffer());
}
return httpContent;
});
if (isJson) {
// if the Publisher is returning JSON then in order for it to be valid JSON for each emitted element
// we must wrap the JSON in array and delimit the emitted items
httpContentPublisher = JsonSubscriber.lift(httpContentPublisher);
}
httpContentPublisher = httpContentPublisher.contextWrite(reactorContext -> reactorContext.put(ServerRequestContext.KEY, request)).doOnNext(httpContent -> context.read()).doAfterTerminate(() -> cleanupRequest(context, request));
return httpContentPublisher;
}
Aggregations