use of io.micronaut.http.netty.stream.StreamedHttpRequest in project micronaut-core by micronaut-projects.
the class DefaultHttpClient method prepareHttpHeaders.
private <I> void prepareHttpHeaders(URI requestURI, io.micronaut.http.HttpRequest<I> request, io.netty.handler.codec.http.HttpRequest nettyRequest, boolean permitsBody, boolean closeConnection) {
HttpHeaders headers = nettyRequest.headers();
if (!headers.contains(HttpHeaderNames.HOST)) {
headers.set(HttpHeaderNames.HOST, getHostHeader(requestURI));
}
// HTTP/2 assumes keep-alive connections
if (httpVersion != io.micronaut.http.HttpVersion.HTTP_2_0) {
if (closeConnection) {
headers.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
} else {
headers.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
}
}
if (permitsBody) {
Optional<I> body = request.getBody();
if (body.isPresent()) {
if (!headers.contains(HttpHeaderNames.CONTENT_TYPE)) {
MediaType mediaType = request.getContentType().orElse(MediaType.APPLICATION_JSON_TYPE);
headers.set(HttpHeaderNames.CONTENT_TYPE, mediaType);
}
if (nettyRequest instanceof FullHttpRequest) {
FullHttpRequest fullHttpRequest = (FullHttpRequest) nettyRequest;
headers.set(HttpHeaderNames.CONTENT_LENGTH, fullHttpRequest.content().readableBytes());
} else {
if (!headers.contains(HttpHeaderNames.CONTENT_LENGTH) && !headers.contains(HttpHeaderNames.TRANSFER_ENCODING)) {
headers.set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED);
}
}
} else if (!(nettyRequest instanceof StreamedHttpRequest)) {
headers.set(HttpHeaderNames.CONTENT_LENGTH, 0);
}
}
}
use of io.micronaut.http.netty.stream.StreamedHttpRequest in project micronaut-core by micronaut-projects.
the class HttpRequestDecoder method decode.
@Override
protected void decode(ChannelHandlerContext ctx, HttpRequest msg, List<Object> out) {
if (LOG.isTraceEnabled()) {
LOG.trace("Server {}:{} Received Request: {} {}", embeddedServer.getHost(), embeddedServer.getPort(), msg.method(), msg.uri());
}
try {
NettyHttpRequest<Object> request = new NettyHttpRequest<>(msg, ctx, conversionService, configuration);
if (httpRequestReceivedEventPublisher != ApplicationEventPublisher.NO_OP) {
try {
ctx.executor().execute(() -> {
try {
httpRequestReceivedEventPublisher.publishEvent(new HttpRequestReceivedEvent(request));
} catch (Exception e) {
if (LOG.isErrorEnabled()) {
LOG.error("Error publishing Http request received event: " + e.getMessage(), e);
}
}
});
} catch (Exception e) {
if (LOG.isErrorEnabled()) {
LOG.error("Error publishing Http request received event: " + e.getMessage(), e);
}
}
}
out.add(request);
} catch (IllegalArgumentException e) {
// this configured the request in the channel as an attribute
new NettyHttpRequest<>(new DefaultHttpRequest(msg.protocolVersion(), msg.method(), "/"), ctx, conversionService, configuration);
final Throwable cause = e.getCause();
ctx.fireExceptionCaught(cause != null ? cause : e);
if (msg instanceof StreamedHttpRequest) {
// discard any data that may come in
((StreamedHttpRequest) msg).closeIfNoSubscriber();
}
}
}
use of io.micronaut.http.netty.stream.StreamedHttpRequest in project micronaut-core by micronaut-projects.
the class CompletableFutureBodyBinder method bind.
@Override
public BindingResult<CompletableFuture> bind(ArgumentConversionContext<CompletableFuture> context, HttpRequest<?> source) {
if (source instanceof NettyHttpRequest) {
NettyHttpRequest nettyHttpRequest = (NettyHttpRequest) source;
io.netty.handler.codec.http.HttpRequest nativeRequest = ((NettyHttpRequest) source).getNativeRequest();
if (nativeRequest instanceof StreamedHttpRequest) {
CompletableFuture future = new CompletableFuture();
Argument<?> targetType = context.getFirstTypeVariable().orElse(Argument.OBJECT_ARGUMENT);
HttpContentProcessor<?> processor = httpContentProcessorResolver.resolve(nettyHttpRequest, targetType);
processor.subscribe(new CompletionAwareSubscriber<Object>() {
@Override
protected void doOnSubscribe(Subscription subscription) {
subscription.request(1);
}
@Override
protected void doOnNext(Object message) {
if (message instanceof ByteBufHolder) {
nettyHttpRequest.addContent((ByteBufHolder) message);
} else {
nettyHttpRequest.setBody(message);
}
subscription.request(1);
}
@Override
protected void doOnError(Throwable t) {
future.completeExceptionally(t);
}
@Override
protected void doOnComplete() {
Optional<Argument<?>> firstTypeParameter = context.getFirstTypeVariable();
if (firstTypeParameter.isPresent()) {
Argument<?> arg = firstTypeParameter.get();
Optional converted = nettyHttpRequest.getBody(arg);
if (converted.isPresent()) {
future.complete(converted.get());
} else {
future.completeExceptionally(new IllegalArgumentException("Cannot bind body to argument type: " + arg.getType().getName()));
}
} else {
future.complete(nettyHttpRequest.getBody().orElse(null));
}
}
});
return () -> Optional.of(future);
} else {
return BindingResult.EMPTY;
}
} else {
return BindingResult.EMPTY;
}
}
use of io.micronaut.http.netty.stream.StreamedHttpRequest in project micronaut-core by micronaut-projects.
the class MultipartBodyArgumentBinder method bind.
@Override
public BindingResult<MultipartBody> bind(ArgumentConversionContext<MultipartBody> context, HttpRequest<?> source) {
if (source instanceof NettyHttpRequest) {
NettyHttpRequest nettyHttpRequest = (NettyHttpRequest) source;
io.netty.handler.codec.http.HttpRequest nativeRequest = nettyHttpRequest.getNativeRequest();
if (nativeRequest instanceof StreamedHttpRequest) {
HttpContentProcessor<?> processor = beanLocator.findBean(HttpContentSubscriberFactory.class, new ConsumesMediaTypeQualifier<>(MediaType.MULTIPART_FORM_DATA_TYPE)).map(factory -> factory.build(nettyHttpRequest)).orElse(new DefaultHttpContentProcessor(nettyHttpRequest, httpServerConfiguration.get()));
// noinspection unchecked
return () -> Optional.of(subscriber -> processor.subscribe(new TypedSubscriber<Object>((Argument) context.getArgument()) {
Subscription s;
AtomicLong partsRequested = new AtomicLong(0);
@Override
protected void doOnSubscribe(Subscription subscription) {
this.s = subscription;
subscriber.onSubscribe(new Subscription() {
@Override
public void request(long n) {
if (partsRequested.getAndUpdate(prev -> prev + n) == 0) {
s.request(n);
}
}
@Override
public void cancel() {
subscription.cancel();
}
});
}
@Override
protected void doOnNext(Object message) {
if (LOG.isTraceEnabled()) {
LOG.trace("Server received streaming message for argument [{}]: {}", context.getArgument(), message);
}
if (message instanceof ByteBufHolder && ((ByteBufHolder) message).content() instanceof EmptyByteBuf) {
return;
}
if (message instanceof HttpData) {
HttpData data = (HttpData) message;
if (data.isCompleted()) {
partsRequested.decrementAndGet();
if (data instanceof FileUpload) {
subscriber.onNext(new NettyCompletedFileUpload((FileUpload) data, false));
} else if (data instanceof Attribute) {
subscriber.onNext(new NettyCompletedAttribute((Attribute) data, false));
}
// If the user didn't release the data, we should
if (data.refCnt() > 0) {
data.release();
}
}
}
if (partsRequested.get() > 0) {
s.request(1);
}
}
@Override
protected void doOnError(Throwable t) {
if (LOG.isTraceEnabled()) {
LOG.trace("Server received error for argument [" + context.getArgument() + "]: " + t.getMessage(), t);
}
try {
subscriber.onError(t);
} finally {
s.cancel();
}
}
@Override
protected void doOnComplete() {
if (LOG.isTraceEnabled()) {
LOG.trace("Done receiving messages for argument: {}", context.getArgument());
}
subscriber.onComplete();
}
}));
}
}
return BindingResult.EMPTY;
}
use of io.micronaut.http.netty.stream.StreamedHttpRequest 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);
}
}
}
Aggregations