Search in sources :

Example 1 with NettyStreamingFileUpload

use of io.micronaut.http.server.netty.multipart.NettyStreamingFileUpload in project micronaut-core by micronaut-projects.

the class RoutingInBoundHandler method buildSubscriber.

private Subscriber<Object> buildSubscriber(NettyHttpRequest<?> request, RouteMatch<?> finalRoute, MonoSink<RouteMatch<?>> emitter) {
    boolean isFormData = request.isFormOrMultipartData();
    if (isFormData) {
        return new CompletionAwareSubscriber<Object>() {

            final boolean alwaysAddContent = request.isFormData();

            RouteMatch<?> routeMatch = finalRoute;

            final AtomicBoolean executed = new AtomicBoolean(false);

            final AtomicLong pressureRequested = new AtomicLong(0);

            final ConcurrentHashMap<String, Sinks.Many<Object>> subjectsByDataName = new ConcurrentHashMap<>();

            final Collection<Sinks.Many<Object>> downstreamSubscribers = Collections.synchronizedList(new ArrayList<>());

            final ConcurrentHashMap<IdentityWrapper, HttpDataReference> dataReferences = new ConcurrentHashMap<>();

            final ConversionService conversionService = ConversionService.SHARED;

            Subscription s;

            final LongConsumer onRequest = num -> pressureRequested.updateAndGet(p -> {
                long newVal = p - num;
                if (newVal < 0) {
                    s.request(num - p);
                    return 0;
                } else {
                    return newVal;
                }
            });

            Flux processFlowable(Sinks.Many<Object> many, HttpDataReference dataReference, boolean controlsFlow) {
                Flux flux = many.asFlux();
                if (controlsFlow) {
                    flux = flux.doOnRequest(onRequest);
                }
                return flux.doAfterTerminate(() -> {
                    if (controlsFlow) {
                        dataReference.destroy();
                    }
                });
            }

            @Override
            protected void doOnSubscribe(Subscription subscription) {
                this.s = subscription;
                subscription.request(1);
            }

            @Override
            protected void doOnNext(Object message) {
                boolean executed = this.executed.get();
                if (message instanceof ByteBufHolder) {
                    if (message instanceof HttpData) {
                        HttpData data = (HttpData) message;
                        if (LOG.isTraceEnabled()) {
                            LOG.trace("Received HTTP Data for request [{}]: {}", request, message);
                        }
                        String name = data.getName();
                        Optional<Argument<?>> requiredInput = routeMatch.getRequiredInput(name);
                        if (requiredInput.isPresent()) {
                            Argument<?> argument = requiredInput.get();
                            Supplier<Object> value;
                            boolean isPublisher = Publishers.isConvertibleToPublisher(argument.getType());
                            boolean chunkedProcessing = false;
                            if (isPublisher) {
                                HttpDataReference dataReference = dataReferences.computeIfAbsent(new IdentityWrapper(data), key -> new HttpDataReference(data));
                                Argument typeVariable;
                                if (StreamingFileUpload.class.isAssignableFrom(argument.getType())) {
                                    typeVariable = ARGUMENT_PART_DATA;
                                } else {
                                    typeVariable = argument.getFirstTypeVariable().orElse(Argument.OBJECT_ARGUMENT);
                                }
                                Class typeVariableType = typeVariable.getType();
                                Sinks.Many<Object> namedSubject = subjectsByDataName.computeIfAbsent(name, key -> makeDownstreamUnicastProcessor());
                                chunkedProcessing = PartData.class.equals(typeVariableType) || Publishers.isConvertibleToPublisher(typeVariableType) || ClassUtils.isJavaLangType(typeVariableType);
                                if (Publishers.isConvertibleToPublisher(typeVariableType)) {
                                    boolean streamingFileUpload = StreamingFileUpload.class.isAssignableFrom(typeVariableType);
                                    if (streamingFileUpload) {
                                        typeVariable = ARGUMENT_PART_DATA;
                                    } else {
                                        typeVariable = typeVariable.getFirstTypeVariable().orElse(Argument.OBJECT_ARGUMENT);
                                    }
                                    dataReference.subject.getAndUpdate(subject -> {
                                        if (subject == null) {
                                            Sinks.Many<Object> childSubject = makeDownstreamUnicastProcessor();
                                            Flux flowable = processFlowable(childSubject, dataReference, true);
                                            if (streamingFileUpload && data instanceof FileUpload) {
                                                namedSubject.tryEmitNext(new NettyStreamingFileUpload((FileUpload) data, serverConfiguration.getMultipart(), getIoExecutor(), (Flux<PartData>) flowable));
                                            } else {
                                                namedSubject.tryEmitNext(flowable);
                                            }
                                            return childSubject;
                                        }
                                        return subject;
                                    });
                                }
                                Sinks.Many<Object> subject;
                                final Sinks.Many<Object> ds = dataReference.subject.get();
                                if (ds != null) {
                                    subject = ds;
                                } else {
                                    subject = namedSubject;
                                }
                                Object part = data;
                                if (chunkedProcessing) {
                                    HttpDataReference.Component component;
                                    try {
                                        component = dataReference.addComponent();
                                        if (component == null) {
                                            s.request(1);
                                            return;
                                        }
                                    } catch (IOException e) {
                                        subject.tryEmitError(e);
                                        s.cancel();
                                        return;
                                    }
                                    part = new NettyPartData(dataReference, component);
                                }
                                if (data instanceof FileUpload && StreamingFileUpload.class.isAssignableFrom(argument.getType())) {
                                    dataReference.upload.getAndUpdate(upload -> {
                                        if (upload == null) {
                                            return new NettyStreamingFileUpload((FileUpload) data, serverConfiguration.getMultipart(), getIoExecutor(), (Flux<PartData>) processFlowable(subject, dataReference, true));
                                        }
                                        return upload;
                                    });
                                }
                                Optional<?> converted = conversionService.convert(part, typeVariable);
                                converted.ifPresent(subject::tryEmitNext);
                                if (data.isCompleted() && chunkedProcessing) {
                                    subject.tryEmitComplete();
                                }
                                value = () -> {
                                    StreamingFileUpload upload = dataReference.upload.get();
                                    if (upload != null) {
                                        return upload;
                                    } else {
                                        return processFlowable(namedSubject, dataReference, dataReference.subject.get() == null);
                                    }
                                };
                            } else {
                                if (data instanceof Attribute && !data.isCompleted()) {
                                    request.addContent(data);
                                    s.request(1);
                                    return;
                                } else {
                                    value = () -> {
                                        if (data.refCnt() > 0) {
                                            return data;
                                        } else {
                                            return null;
                                        }
                                    };
                                }
                            }
                            if (!executed) {
                                String argumentName = argument.getName();
                                if (!routeMatch.isSatisfied(argumentName)) {
                                    Object fulfillParamter = value.get();
                                    routeMatch = routeMatch.fulfill(Collections.singletonMap(argumentName, fulfillParamter));
                                    // the data to the request ensures it is cleaned up after the route completes.
                                    if (!alwaysAddContent && fulfillParamter instanceof ByteBufHolder) {
                                        request.addContent((ByteBufHolder) fulfillParamter);
                                    }
                                }
                                if (isPublisher && chunkedProcessing) {
                                    // accounting for the previous request
                                    pressureRequested.incrementAndGet();
                                }
                                if (routeMatch.isExecutable() || message instanceof LastHttpContent) {
                                    executeRoute();
                                    executed = true;
                                }
                            }
                            if (alwaysAddContent) {
                                request.addContent(data);
                            }
                            if (!executed || !chunkedProcessing) {
                                s.request(1);
                            }
                        } else {
                            request.addContent(data);
                            s.request(1);
                        }
                    } else {
                        request.addContent((ByteBufHolder) message);
                        s.request(1);
                    }
                } else {
                    ((NettyHttpRequest) request).setBody(message);
                    s.request(1);
                }
            }

            @Override
            protected void doOnError(Throwable t) {
                s.cancel();
                for (Sinks.Many<Object> subject : downstreamSubscribers) {
                    subject.tryEmitError(t);
                }
                emitter.error(t);
            }

            @Override
            protected void doOnComplete() {
                for (Sinks.Many<Object> subject : downstreamSubscribers) {
                    // subjects will ignore the onComplete if they're already done
                    subject.tryEmitComplete();
                }
                executeRoute();
            }

            private Sinks.Many<Object> makeDownstreamUnicastProcessor() {
                Sinks.Many<Object> processor = Sinks.many().unicast().onBackpressureBuffer();
                downstreamSubscribers.add(processor);
                return processor;
            }

            private void executeRoute() {
                if (executed.compareAndSet(false, true)) {
                    emitter.success(routeMatch);
                }
            }
        };
    } else {
        return new CompletionAwareSubscriber<Object>() {

            private Subscription s;

            private RouteMatch<?> routeMatch = finalRoute;

            private AtomicBoolean executed = new AtomicBoolean(false);

            @Override
            protected void doOnSubscribe(Subscription subscription) {
                this.s = subscription;
                subscription.request(1);
            }

            @Override
            protected void doOnNext(Object message) {
                if (message instanceof ByteBufHolder) {
                    request.addContent((ByteBufHolder) message);
                    s.request(1);
                } else {
                    ((NettyHttpRequest) request).setBody(message);
                    s.request(1);
                }
            }

            @Override
            protected void doOnError(Throwable t) {
                s.cancel();
                emitter.error(t);
            }

            @Override
            protected void doOnComplete() {
                if (executed.compareAndSet(false, true)) {
                    emitter.success(routeMatch);
                }
            }
        };
    }
}
Also used : Publishers(io.micronaut.core.async.publisher.Publishers) HttpRequestTerminatedEvent(io.micronaut.http.context.event.HttpRequestTerminatedEvent) HttpHeaders(io.micronaut.http.HttpHeaders) Internal(io.micronaut.core.annotation.Internal) NettyStreamedFileCustomizableResponseType(io.micronaut.http.server.netty.types.files.NettyStreamedFileCustomizableResponseType) ByteBufHolder(io.netty.buffer.ByteBufHolder) HttpStatus(io.micronaut.http.HttpStatus) IdleState(io.netty.handler.timeout.IdleState) NettySystemFileCustomizableResponseType(io.micronaut.http.server.netty.types.files.NettySystemFileCustomizableResponseType) NettyPartData(io.micronaut.http.server.netty.multipart.NettyPartData) TextPlainCodec(io.micronaut.runtime.http.codec.TextPlainCodec) IdleStateEvent(io.netty.handler.timeout.IdleStateEvent) Set(java.util.Set) RequestArgumentSatisfier(io.micronaut.http.server.binding.RequestArgumentSatisfier) SSLException(javax.net.ssl.SSLException) RouteInfo(io.micronaut.web.router.RouteInfo) Writable(io.micronaut.core.io.Writable) Body(io.micronaut.http.annotation.Body) DefaultFullHttpResponse(io.netty.handler.codec.http.DefaultFullHttpResponse) Http2Error(io.netty.handler.codec.http2.Http2Error) InternalServerException(io.micronaut.http.server.exceptions.InternalServerException) RouteMatch(io.micronaut.web.router.RouteMatch) NettyByteBufferFactory(io.micronaut.buffer.netty.NettyByteBufferFactory) NettyMutableHttpResponse(io.micronaut.http.netty.NettyMutableHttpResponse) MonoSink(reactor.core.publisher.MonoSink) Supplier(java.util.function.Supplier) ArrayList(java.util.ArrayList) UriRouteMatch(io.micronaut.web.router.UriRouteMatch) HttpData(io.netty.handler.codec.http.multipart.HttpData) Nullable(io.micronaut.core.annotation.Nullable) DuplicateRouteException(io.micronaut.web.router.exceptions.DuplicateRouteException) BiConsumer(java.util.function.BiConsumer) ByteBuffer(io.micronaut.core.io.buffer.ByteBuffer) Argument(io.micronaut.core.type.Argument) HttpRequest(io.micronaut.http.HttpRequest) ConversionService(io.micronaut.core.convert.ConversionService) ServerRequestContext(io.micronaut.http.context.ServerRequestContext) ErrorResponseProcessor(io.micronaut.http.server.exceptions.response.ErrorResponseProcessor) MediaTypeCodecRegistry(io.micronaut.http.codec.MediaTypeCodecRegistry) HttpContent(io.netty.handler.codec.http.HttpContent) FileUpload(io.netty.handler.codec.http.multipart.FileUpload) ClosedChannelException(java.nio.channels.ClosedChannelException) Publisher(org.reactivestreams.Publisher) Mono(reactor.core.publisher.Mono) IOException(java.io.IOException) NettyHttpResponseBuilder(io.micronaut.http.netty.NettyHttpResponseBuilder) File(java.io.File) AbstractNettyHttpRequest(io.micronaut.http.netty.AbstractNettyHttpRequest) DefaultHttpContent(io.netty.handler.codec.http.DefaultHttpContent) Flux(reactor.core.publisher.Flux) AtomicLong(java.util.concurrent.atomic.AtomicLong) Paths(java.nio.file.Paths) SimpleChannelInboundHandler(io.netty.channel.SimpleChannelInboundHandler) HttpHeaderNames(io.netty.handler.codec.http.HttpHeaderNames) Future(io.netty.util.concurrent.Future) RouteExecutor(io.micronaut.http.server.RouteExecutor) Sinks(reactor.core.publisher.Sinks) StreamingFileUpload(io.micronaut.http.multipart.StreamingFileUpload) URL(java.net.URL) URISyntaxException(java.net.URISyntaxException) LoggerFactory(org.slf4j.LoggerFactory) NettyHttpServerConfiguration(io.micronaut.http.server.netty.configuration.NettyHttpServerConfiguration) FileCustomizableResponseType(io.micronaut.http.server.types.files.FileCustomizableResponseType) Unpooled(io.netty.buffer.Unpooled) CompletionAwareSubscriber(io.micronaut.core.async.subscriber.CompletionAwareSubscriber) PartData(io.micronaut.http.multipart.PartData) NettyCustomizableResponseTypeHandler(io.micronaut.http.server.netty.types.NettyCustomizableResponseTypeHandler) StaticResourceResolver(io.micronaut.web.router.resource.StaticResourceResolver) MediaType(io.micronaut.http.MediaType) Http2Exception(io.netty.handler.codec.http2.Http2Exception) ReferenceCounted(io.micronaut.core.io.buffer.ReferenceCounted) HttpResponse(io.micronaut.http.HttpResponse) ClassUtils(io.micronaut.core.reflect.ClassUtils) Collection(java.util.Collection) MethodBasedRouteMatch(io.micronaut.web.router.MethodBasedRouteMatch) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) MutableHttpResponse(io.micronaut.http.MutableHttpResponse) MutableHttpHeaders(io.micronaut.http.MutableHttpHeaders) NettyCustomizableResponseTypeHandlerRegistry(io.micronaut.http.server.netty.types.NettyCustomizableResponseTypeHandlerRegistry) TooLongFrameException(io.netty.handler.codec.TooLongFrameException) HttpResponseStatus(io.netty.handler.codec.http.HttpResponseStatus) Collectors(java.util.stream.Collectors) ByteBufOutputStream(io.netty.buffer.ByteBufOutputStream) Attribute(io.netty.handler.codec.http.multipart.Attribute) StreamedHttpRequest(io.micronaut.http.netty.stream.StreamedHttpRequest) DecoderResult(io.netty.handler.codec.DecoderResult) List(java.util.List) DefaultHttpHeaders(io.netty.handler.codec.http.DefaultHttpHeaders) Optional(java.util.Optional) HttpAttributes(io.micronaut.http.HttpAttributes) Pattern(java.util.regex.Pattern) HttpVersion(io.netty.handler.codec.http.HttpVersion) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) AtomicReference(java.util.concurrent.atomic.AtomicReference) Sharable(io.netty.channel.ChannelHandler.Sharable) LastHttpContent(io.netty.handler.codec.http.LastHttpContent) HashSet(java.util.HashSet) ErrorContext(io.micronaut.http.server.exceptions.response.ErrorContext) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) ByteBuf(io.netty.buffer.ByteBuf) Subscriber(org.reactivestreams.Subscriber) ExecutorService(java.util.concurrent.ExecutorService) HttpMethod(io.micronaut.http.HttpMethod) MediaTypeCodec(io.micronaut.http.codec.MediaTypeCodec) Logger(org.slf4j.Logger) HttpHeaderValues(io.netty.handler.codec.http.HttpHeaderValues) ApplicationEventPublisher(io.micronaut.context.event.ApplicationEventPublisher) GenericFutureListener(io.netty.util.concurrent.GenericFutureListener) NettyStreamingFileUpload(io.micronaut.http.server.netty.multipart.NettyStreamingFileUpload) LongConsumer(java.util.function.LongConsumer) NonNull(io.micronaut.core.annotation.NonNull) CollectionUtils(io.micronaut.core.util.CollectionUtils) Subscription(org.reactivestreams.Subscription) Router(io.micronaut.web.router.Router) Collections(java.util.Collections) JsonSubscriber(io.micronaut.http.netty.stream.JsonSubscriber) Argument(io.micronaut.core.type.Argument) Attribute(io.netty.handler.codec.http.multipart.Attribute) ArrayList(java.util.ArrayList) CompletionAwareSubscriber(io.micronaut.core.async.subscriber.CompletionAwareSubscriber) ConversionService(io.micronaut.core.convert.ConversionService) HttpData(io.netty.handler.codec.http.multipart.HttpData) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Subscription(org.reactivestreams.Subscription) FileUpload(io.netty.handler.codec.http.multipart.FileUpload) StreamingFileUpload(io.micronaut.http.multipart.StreamingFileUpload) NettyStreamingFileUpload(io.micronaut.http.server.netty.multipart.NettyStreamingFileUpload) Sinks(reactor.core.publisher.Sinks) Flux(reactor.core.publisher.Flux) StreamingFileUpload(io.micronaut.http.multipart.StreamingFileUpload) NettyStreamingFileUpload(io.micronaut.http.server.netty.multipart.NettyStreamingFileUpload) IOException(java.io.IOException) LastHttpContent(io.netty.handler.codec.http.LastHttpContent) RouteMatch(io.micronaut.web.router.RouteMatch) UriRouteMatch(io.micronaut.web.router.UriRouteMatch) MethodBasedRouteMatch(io.micronaut.web.router.MethodBasedRouteMatch) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) AtomicLong(java.util.concurrent.atomic.AtomicLong) LongConsumer(java.util.function.LongConsumer) NettyPartData(io.micronaut.http.server.netty.multipart.NettyPartData) NettyPartData(io.micronaut.http.server.netty.multipart.NettyPartData) PartData(io.micronaut.http.multipart.PartData) Collection(java.util.Collection) ByteBufHolder(io.netty.buffer.ByteBufHolder) NettyStreamingFileUpload(io.micronaut.http.server.netty.multipart.NettyStreamingFileUpload) AbstractNettyHttpRequest(io.micronaut.http.netty.AbstractNettyHttpRequest)

Aggregations

NettyByteBufferFactory (io.micronaut.buffer.netty.NettyByteBufferFactory)1 ApplicationEventPublisher (io.micronaut.context.event.ApplicationEventPublisher)1 Internal (io.micronaut.core.annotation.Internal)1 NonNull (io.micronaut.core.annotation.NonNull)1 Nullable (io.micronaut.core.annotation.Nullable)1 Publishers (io.micronaut.core.async.publisher.Publishers)1 CompletionAwareSubscriber (io.micronaut.core.async.subscriber.CompletionAwareSubscriber)1 ConversionService (io.micronaut.core.convert.ConversionService)1 Writable (io.micronaut.core.io.Writable)1 ByteBuffer (io.micronaut.core.io.buffer.ByteBuffer)1 ReferenceCounted (io.micronaut.core.io.buffer.ReferenceCounted)1 ClassUtils (io.micronaut.core.reflect.ClassUtils)1 Argument (io.micronaut.core.type.Argument)1 CollectionUtils (io.micronaut.core.util.CollectionUtils)1 HttpAttributes (io.micronaut.http.HttpAttributes)1 HttpHeaders (io.micronaut.http.HttpHeaders)1 HttpMethod (io.micronaut.http.HttpMethod)1 HttpRequest (io.micronaut.http.HttpRequest)1 HttpResponse (io.micronaut.http.HttpResponse)1 HttpStatus (io.micronaut.http.HttpStatus)1