Search in sources :

Example 1 with TypedSubscriber

use of io.micronaut.core.async.subscriber.TypedSubscriber 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;
}
Also used : ArgumentConversionContext(io.micronaut.core.convert.ArgumentConversionContext) LoggerFactory(org.slf4j.LoggerFactory) DefaultHttpContentProcessor(io.micronaut.http.server.netty.DefaultHttpContentProcessor) Internal(io.micronaut.core.annotation.Internal) ByteBufHolder(io.netty.buffer.ByteBufHolder) HttpContentProcessor(io.micronaut.http.server.netty.HttpContentProcessor) TypedSubscriber(io.micronaut.core.async.subscriber.TypedSubscriber) NonBlockingBodyArgumentBinder(io.micronaut.http.bind.binders.NonBlockingBodyArgumentBinder) MediaType(io.micronaut.http.MediaType) HttpData(io.netty.handler.codec.http.multipart.HttpData) Argument(io.micronaut.core.type.Argument) HttpRequest(io.micronaut.http.HttpRequest) BeanLocator(io.micronaut.context.BeanLocator) NettyHttpRequest(io.micronaut.http.server.netty.NettyHttpRequest) NettyHttpServer(io.micronaut.http.server.netty.NettyHttpServer) Logger(org.slf4j.Logger) MultipartBody(io.micronaut.http.server.multipart.MultipartBody) FileUpload(io.netty.handler.codec.http.multipart.FileUpload) HttpServerConfiguration(io.micronaut.http.server.HttpServerConfiguration) HttpContentSubscriberFactory(io.micronaut.http.server.netty.HttpContentSubscriberFactory) Attribute(io.netty.handler.codec.http.multipart.Attribute) AtomicLong(java.util.concurrent.atomic.AtomicLong) StreamedHttpRequest(io.micronaut.http.netty.stream.StreamedHttpRequest) EmptyByteBuf(io.netty.buffer.EmptyByteBuf) BeanProvider(io.micronaut.context.BeanProvider) Subscription(org.reactivestreams.Subscription) Optional(java.util.Optional) ConsumesMediaTypeQualifier(io.micronaut.web.router.qualifier.ConsumesMediaTypeQualifier) EmptyByteBuf(io.netty.buffer.EmptyByteBuf) Attribute(io.netty.handler.codec.http.multipart.Attribute) StreamedHttpRequest(io.micronaut.http.netty.stream.StreamedHttpRequest) AtomicLong(java.util.concurrent.atomic.AtomicLong) DefaultHttpContentProcessor(io.micronaut.http.server.netty.DefaultHttpContentProcessor) HttpData(io.netty.handler.codec.http.multipart.HttpData) ByteBufHolder(io.netty.buffer.ByteBufHolder) NettyHttpRequest(io.micronaut.http.server.netty.NettyHttpRequest) Subscription(org.reactivestreams.Subscription) TypedSubscriber(io.micronaut.core.async.subscriber.TypedSubscriber) FileUpload(io.netty.handler.codec.http.multipart.FileUpload)

Example 2 with TypedSubscriber

use of io.micronaut.core.async.subscriber.TypedSubscriber in project micronaut-core by micronaut-projects.

the class PublisherBodyBinder method bind.

@Override
public BindingResult<Publisher> bind(ArgumentConversionContext<Publisher> context, HttpRequest<?> source) {
    if (source instanceof NettyHttpRequest) {
        NettyHttpRequest nettyHttpRequest = (NettyHttpRequest) source;
        io.netty.handler.codec.http.HttpRequest nativeRequest = nettyHttpRequest.getNativeRequest();
        if (nativeRequest instanceof StreamedHttpRequest) {
            Argument<?> targetType = context.getFirstTypeVariable().orElse(Argument.OBJECT_ARGUMENT);
            HttpContentProcessor<?> processor = httpContentProcessorResolver.resolve(nettyHttpRequest, targetType);
            // noinspection unchecked
            return () -> Optional.of(subscriber -> processor.subscribe(new TypedSubscriber<Object>((Argument) context.getArgument()) {

                Subscription s;

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

                @Override
                protected void doOnNext(Object message) {
                    if (LOG.isTraceEnabled()) {
                        LOG.trace("Server received streaming message for argument [{}]: {}", context.getArgument(), message);
                    }
                    if (message instanceof ByteBufHolder) {
                        message = ((ByteBufHolder) message).content();
                        if (message instanceof EmptyByteBuf) {
                            return;
                        }
                    }
                    ArgumentConversionContext<?> conversionContext = context.with(targetType);
                    Optional<?> converted = conversionService.convert(message, conversionContext);
                    if (converted.isPresent()) {
                        subscriber.onNext(converted.get());
                    } else {
                        try {
                            Optional<ConversionError> lastError = conversionContext.getLastError();
                            if (lastError.isPresent()) {
                                if (LOG.isDebugEnabled()) {
                                    LOG.debug("Cannot convert message for argument [" + context.getArgument() + "] and value: " + message, lastError.get());
                                }
                                subscriber.onError(new ConversionErrorException(context.getArgument(), lastError.get()));
                            } else {
                                if (LOG.isDebugEnabled()) {
                                    LOG.debug("Cannot convert message for argument [{}] and value: {}", context.getArgument(), message);
                                }
                                subscriber.onError(UnsatisfiedRouteException.create(context.getArgument()));
                            }
                        } finally {
                            s.cancel();
                        }
                    }
                    if (message instanceof ReferenceCounted) {
                        ((ReferenceCounted) message).release();
                    }
                }

                @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;
}
Also used : EmptyByteBuf(io.netty.buffer.EmptyByteBuf) ConversionError(io.micronaut.core.convert.ConversionError) ConversionErrorException(io.micronaut.core.convert.exceptions.ConversionErrorException) StreamedHttpRequest(io.micronaut.http.netty.stream.StreamedHttpRequest) ByteBufHolder(io.netty.buffer.ByteBufHolder) NettyHttpRequest(io.micronaut.http.server.netty.NettyHttpRequest) Subscription(org.reactivestreams.Subscription) TypedSubscriber(io.micronaut.core.async.subscriber.TypedSubscriber) ReferenceCounted(io.netty.util.ReferenceCounted)

Example 3 with TypedSubscriber

use of io.micronaut.core.async.subscriber.TypedSubscriber in project micronaut-core by micronaut-projects.

the class JsonContentProcessor method doOnSubscribe.

@Override
protected void doOnSubscribe(Subscription subscription, Subscriber<? super JsonNode> subscriber) {
    if (parentSubscription == null) {
        return;
    }
    boolean streamArray = false;
    boolean isJsonStream = nettyHttpRequest.getContentType().map(mediaType -> mediaType.equals(MediaType.APPLICATION_JSON_STREAM_TYPE)).orElse(false);
    if (subscriber instanceof TypedSubscriber) {
        TypedSubscriber typedSubscriber = (TypedSubscriber) subscriber;
        Argument typeArgument = typedSubscriber.getTypeArgument();
        Class targetType = typeArgument.getType();
        if (Publishers.isConvertibleToPublisher(targetType) && !Publishers.isSingle(targetType)) {
            Optional<Argument<?>> genericArgument = typeArgument.getFirstTypeVariable();
            if (genericArgument.isPresent() && !Iterable.class.isAssignableFrom(genericArgument.get().getType()) && !isJsonStream) {
                // if the generic argument is not a iterable type them stream the array into the publisher
                streamArray = true;
            }
        }
    }
    this.jacksonProcessor = jsonMapper.createReactiveParser(p -> {
    }, streamArray);
    this.jacksonProcessor.subscribe(new CompletionAwareSubscriber<JsonNode>() {

        @Override
        protected void doOnSubscribe(Subscription jsonSubscription) {
            Subscription childSubscription = new Subscription() {

                boolean first = true;

                @Override
                public synchronized void request(long n) {
                    // find a better way in the future
                    if (first) {
                        jsonSubscription.request(n < Long.MAX_VALUE ? n + 1 : n);
                        parentSubscription.request(n < Long.MAX_VALUE ? n + 1 : n);
                    } else {
                        jsonSubscription.request(n);
                        parentSubscription.request(n);
                    }
                }

                @Override
                public synchronized void cancel() {
                    jsonSubscription.cancel();
                    parentSubscription.cancel();
                }
            };
            subscriber.onSubscribe(childSubscription);
        }

        @Override
        protected void doOnNext(JsonNode message) {
            subscriber.onNext(message);
        }

        @Override
        protected void doOnError(Throwable t) {
            subscriber.onError(t);
        }

        @Override
        protected void doOnComplete() {
            subscriber.onComplete();
        }
    });
    jacksonProcessor.onSubscribe(subscription);
}
Also used : Publishers(io.micronaut.core.async.publisher.Publishers) JsonNode(io.micronaut.json.tree.JsonNode) HttpServerConfiguration(io.micronaut.http.server.HttpServerConfiguration) Processor(org.reactivestreams.Processor) Internal(io.micronaut.core.annotation.Internal) ByteBufHolder(io.netty.buffer.ByteBufHolder) ByteBufUtil(io.netty.buffer.ByteBufUtil) CompletionAwareSubscriber(io.micronaut.core.async.subscriber.CompletionAwareSubscriber) ByteBuf(io.netty.buffer.ByteBuf) TypedSubscriber(io.micronaut.core.async.subscriber.TypedSubscriber) MediaType(io.micronaut.http.MediaType) ReferenceCountUtil(io.netty.util.ReferenceCountUtil) Subscription(org.reactivestreams.Subscription) JsonMapper(io.micronaut.json.JsonMapper) AbstractHttpContentProcessor(io.micronaut.http.server.netty.AbstractHttpContentProcessor) Optional(java.util.Optional) Argument(io.micronaut.core.type.Argument) Subscriber(org.reactivestreams.Subscriber) NettyHttpRequest(io.micronaut.http.server.netty.NettyHttpRequest) Argument(io.micronaut.core.type.Argument) JsonNode(io.micronaut.json.tree.JsonNode) Subscription(org.reactivestreams.Subscription) TypedSubscriber(io.micronaut.core.async.subscriber.TypedSubscriber)

Aggregations

TypedSubscriber (io.micronaut.core.async.subscriber.TypedSubscriber)3 NettyHttpRequest (io.micronaut.http.server.netty.NettyHttpRequest)3 ByteBufHolder (io.netty.buffer.ByteBufHolder)3 Subscription (org.reactivestreams.Subscription)3 Internal (io.micronaut.core.annotation.Internal)2 Argument (io.micronaut.core.type.Argument)2 MediaType (io.micronaut.http.MediaType)2 StreamedHttpRequest (io.micronaut.http.netty.stream.StreamedHttpRequest)2 HttpServerConfiguration (io.micronaut.http.server.HttpServerConfiguration)2 EmptyByteBuf (io.netty.buffer.EmptyByteBuf)2 Optional (java.util.Optional)2 BeanLocator (io.micronaut.context.BeanLocator)1 BeanProvider (io.micronaut.context.BeanProvider)1 Publishers (io.micronaut.core.async.publisher.Publishers)1 CompletionAwareSubscriber (io.micronaut.core.async.subscriber.CompletionAwareSubscriber)1 ArgumentConversionContext (io.micronaut.core.convert.ArgumentConversionContext)1 ConversionError (io.micronaut.core.convert.ConversionError)1 ConversionErrorException (io.micronaut.core.convert.exceptions.ConversionErrorException)1 HttpRequest (io.micronaut.http.HttpRequest)1 NonBlockingBodyArgumentBinder (io.micronaut.http.bind.binders.NonBlockingBodyArgumentBinder)1