Search in sources :

Example 1 with StreamingFileUpload

use of io.micronaut.http.multipart.StreamingFileUpload in project zuliasearch by zuliaio.

the class AssociatedController method transferToStream.

// work around for
// https://github.com/micronaut-projects/micronaut-core/issues/6084
public static Publisher<Boolean> transferToStream(ExecutorService ioExecutor, StreamingFileUpload fileUpload, OutputStream outputStream) {
    return Mono.<Boolean>create(emitter -> Flux.from(fileUpload).subscribeOn(Schedulers.fromExecutorService(ioExecutor)).subscribe(new Subscriber<>() {

        Subscription subscription;

        @Override
        public void onSubscribe(Subscription s) {
            subscription = s;
            subscription.request(1);
        }

        @Override
        public void onNext(PartData o) {
            try {
                outputStream.write(o.getBytes());
                subscription.request(1);
            } catch (IOException e) {
                handleError(e);
            }
        }

        @Override
        public void onError(Throwable t) {
            emitter.error(t);
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                System.err.println("Failed to close file stream : " + fileUpload.getName());
            }
        }

        @Override
        public void onComplete() {
            try {
                outputStream.close();
                emitter.success(true);
            } catch (IOException e) {
                System.err.println("Failed to close file stream : " + fileUpload.getName());
                emitter.success(false);
            }
        }

        private void handleError(Throwable t) {
            subscription.cancel();
            onError(new MultipartException("Error transferring file: " + fileUpload.getName(), t));
        }
    })).flux();
}
Also used : Document(org.bson.Document) JsonObject(com.google.gson.JsonObject) StreamingFileUpload(io.micronaut.http.multipart.StreamingFileUpload) StreamedFile(io.micronaut.http.server.types.files.StreamedFile) Produces(io.micronaut.http.annotation.Produces) Level(java.util.logging.Level) TaskExecutors(io.micronaut.scheduling.TaskExecutors) PartData(io.micronaut.http.multipart.PartData) ByteArrayInputStream(java.io.ByteArrayInputStream) MediaType(io.micronaut.http.MediaType) Nullable(io.micronaut.core.annotation.Nullable) Map(java.util.Map) HttpResponse(io.micronaut.http.HttpResponse) Schedulers(reactor.core.scheduler.Schedulers) ZuliaNodeProvider(io.zulia.server.util.ZuliaNodeProvider) MultipartException(io.micronaut.http.multipart.MultipartException) ZuliaIndexManager(io.zulia.server.index.ZuliaIndexManager) Subscriber(org.reactivestreams.Subscriber) ExecutorService(java.util.concurrent.ExecutorService) Get(io.micronaut.http.annotation.Get) ZuliaConstants(io.zulia.ZuliaConstants) ZuliaBase(io.zulia.message.ZuliaBase) OutputStream(java.io.OutputStream) Controller(io.micronaut.http.annotation.Controller) Publisher(org.reactivestreams.Publisher) MutableHttpResponse(io.micronaut.http.MutableHttpResponse) QueryValue(io.micronaut.http.annotation.QueryValue) Mono(reactor.core.publisher.Mono) IOException(java.io.IOException) Logger(java.util.logging.Logger) Post(io.micronaut.http.annotation.Post) Flux(reactor.core.publisher.Flux) JsonArray(com.google.gson.JsonArray) List(java.util.List) Writable(io.micronaut.core.io.Writable) Subscription(org.reactivestreams.Subscription) Inject(jakarta.inject.Inject) Named(jakarta.inject.Named) InputStream(java.io.InputStream) MultipartException(io.micronaut.http.multipart.MultipartException) PartData(io.micronaut.http.multipart.PartData) IOException(java.io.IOException) Subscription(org.reactivestreams.Subscription)

Example 2 with StreamingFileUpload

use of io.micronaut.http.multipart.StreamingFileUpload 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

Nullable (io.micronaut.core.annotation.Nullable)2 Writable (io.micronaut.core.io.Writable)2 HttpResponse (io.micronaut.http.HttpResponse)2 MediaType (io.micronaut.http.MediaType)2 MutableHttpResponse (io.micronaut.http.MutableHttpResponse)2 PartData (io.micronaut.http.multipart.PartData)2 StreamingFileUpload (io.micronaut.http.multipart.StreamingFileUpload)2 IOException (java.io.IOException)2 List (java.util.List)2 ExecutorService (java.util.concurrent.ExecutorService)2 Publisher (org.reactivestreams.Publisher)2 Subscriber (org.reactivestreams.Subscriber)2 Subscription (org.reactivestreams.Subscription)2 Flux (reactor.core.publisher.Flux)2 Mono (reactor.core.publisher.Mono)2 JsonArray (com.google.gson.JsonArray)1 JsonObject (com.google.gson.JsonObject)1 NettyByteBufferFactory (io.micronaut.buffer.netty.NettyByteBufferFactory)1 ApplicationEventPublisher (io.micronaut.context.event.ApplicationEventPublisher)1 Internal (io.micronaut.core.annotation.Internal)1