Search in sources :

Example 1 with CHUNKED

use of io.servicetalk.http.api.HttpHeaderValues.CHUNKED in project servicetalk by apple.

the class BlockingStreamingToStreamingService method handle.

@Override
public Single<StreamingHttpResponse> handle(final HttpServiceContext ctx, final StreamingHttpRequest request, final StreamingHttpResponseFactory responseFactory) {
    return new Single<StreamingHttpResponse>() {

        @Override
        protected void handleSubscribe(final Subscriber<? super StreamingHttpResponse> subscriber) {
            final ThreadInterruptingCancellable tiCancellable = new ThreadInterruptingCancellable(currentThread());
            try {
                subscriber.onSubscribe(tiCancellable);
            } catch (Throwable cause) {
                handleExceptionFromOnSubscribe(subscriber, cause);
                return;
            }
            // This exists to help users with error propagation. If the user closes the payloadWriter and they throw
            // (e.g. try-with-resources) this processor is merged with the payloadWriter Publisher so the error will
            // still be propagated.
            final Processor exceptionProcessor = newCompletableProcessor();
            final BufferHttpPayloadWriter payloadWriter = new BufferHttpPayloadWriter(ctx.headersFactory().newTrailers());
            DefaultBlockingStreamingHttpServerResponse response = null;
            try {
                final Consumer<DefaultHttpResponseMetaData> sendMeta = (metaData) -> {
                    final DefaultStreamingHttpResponse result;
                    try {
                        // transfer-encoding takes precedence over content-length.
                        // > When a message does not have a Transfer-Encoding header field, a
                        // Content-Length header field can provide the anticipated size.
                        // https://tools.ietf.org/html/rfc7230#section-3.3.2
                        final HttpHeaders headers = metaData.headers();
                        final HttpProtocolVersion version = metaData.version();
                        boolean addTrailers = version.major() > 1 || isTransferEncodingChunked(headers);
                        if (!addTrailers && h1TrailersSupported(version) && !hasContentLength(headers) && // breaks our HttpResponseDecoder
                        !HEAD.equals(request.method())) {
                            // this is likely not supported in http/1.0 and it is possible that a response has
                            // neither header and the connection close indicates the end of the response.
                            // https://tools.ietf.org/html/rfc7230#section-3.3.3
                            headers.add(TRANSFER_ENCODING, CHUNKED);
                            addTrailers = true;
                        }
                        Publisher<Object> messageBody = fromSource(exceptionProcessor).merge(payloadWriter.connect());
                        if (addTrailers) {
                            messageBody = messageBody.concat(succeeded(payloadWriter.trailers()));
                        }
                        messageBody = messageBody.beforeSubscription(() -> new Subscription() {

                            @Override
                            public void request(final long n) {
                            }

                            @Override
                            public void cancel() {
                                tiCancellable.cancel();
                            }
                        });
                        result = new DefaultStreamingHttpResponse(metaData.status(), version, headers, metaData.context0(), ctx.executionContext().bufferAllocator(), messageBody, forTransportReceive(false, version, headers), ctx.headersFactory());
                    } catch (Throwable t) {
                        subscriber.onError(t);
                        throw t;
                    }
                    subscriber.onSuccess(result);
                };
                response = new DefaultBlockingStreamingHttpServerResponse(OK, request.version(), ctx.headersFactory().newHeaders(), payloadWriter, ctx.executionContext().bufferAllocator(), sendMeta);
                original.handle(ctx, request.toBlockingStreamingRequest(), response);
                // The user code has returned successfully, complete the processor so the response stream can
                // complete. If the user handles the request asynchronously (e.g. on another thread) they are
                // responsible for closing the payloadWriter.
                exceptionProcessor.onComplete();
            } catch (Throwable cause) {
                tiCancellable.setDone(cause);
                if (response == null || response.markMetaSent()) {
                    safeOnError(subscriber, cause);
                } else {
                    try {
                        exceptionProcessor.onError(cause);
                    } finally {
                        safeClose(payloadWriter, cause);
                    }
                }
                return;
            }
            tiCancellable.setDone();
        }
    };
}
Also used : HttpProtocolVersion.h1TrailersSupported(io.servicetalk.http.api.HttpProtocolVersion.h1TrailersSupported) DefaultPayloadInfo.forTransportReceive(io.servicetalk.http.api.DefaultPayloadInfo.forTransportReceive) Publisher(io.servicetalk.concurrent.api.Publisher) Subscriber(io.servicetalk.concurrent.SingleSource.Subscriber) ConnectablePayloadWriter(io.servicetalk.concurrent.api.internal.ConnectablePayloadWriter) PayloadWriterUtils.safeClose(io.servicetalk.oio.api.internal.PayloadWriterUtils.safeClose) Thread.currentThread(java.lang.Thread.currentThread) ThreadInterruptingCancellable(io.servicetalk.concurrent.internal.ThreadInterruptingCancellable) SourceAdapters.fromSource(io.servicetalk.concurrent.api.SourceAdapters.fromSource) HeaderUtils.isTransferEncodingChunked(io.servicetalk.http.api.HeaderUtils.isTransferEncodingChunked) HttpExecutionStrategies.defaultStrategy(io.servicetalk.http.api.HttpExecutionStrategies.defaultStrategy) HEAD(io.servicetalk.http.api.HttpRequestMethod.HEAD) Objects.requireNonNull(java.util.Objects.requireNonNull) SubscriberUtils.safeOnError(io.servicetalk.concurrent.internal.SubscriberUtils.safeOnError) Processors.newCompletableProcessor(io.servicetalk.concurrent.api.Processors.newCompletableProcessor) CHUNKED(io.servicetalk.http.api.HttpHeaderValues.CHUNKED) SubscriberUtils.handleExceptionFromOnSubscribe(io.servicetalk.concurrent.internal.SubscriberUtils.handleExceptionFromOnSubscribe) Single(io.servicetalk.concurrent.api.Single) Completable(io.servicetalk.concurrent.api.Completable) OFFLOAD_RECEIVE_META_STRATEGY(io.servicetalk.http.api.DefaultHttpExecutionStrategy.OFFLOAD_RECEIVE_META_STRATEGY) TRANSFER_ENCODING(io.servicetalk.http.api.HttpHeaderNames.TRANSFER_ENCODING) IOException(java.io.IOException) Subscription(io.servicetalk.concurrent.PublisherSource.Subscription) OK(io.servicetalk.http.api.HttpResponseStatus.OK) Consumer(java.util.function.Consumer) BlockingUtils.blockingToCompletable(io.servicetalk.http.api.BlockingUtils.blockingToCompletable) HeaderUtils.hasContentLength(io.servicetalk.http.api.HeaderUtils.hasContentLength) Buffer(io.servicetalk.buffer.api.Buffer) Processor(io.servicetalk.concurrent.CompletableSource.Processor) Processors.newCompletableProcessor(io.servicetalk.concurrent.api.Processors.newCompletableProcessor) Processor(io.servicetalk.concurrent.CompletableSource.Processor) ThreadInterruptingCancellable(io.servicetalk.concurrent.internal.ThreadInterruptingCancellable) Publisher(io.servicetalk.concurrent.api.Publisher) Single(io.servicetalk.concurrent.api.Single) Subscriber(io.servicetalk.concurrent.SingleSource.Subscriber) Subscription(io.servicetalk.concurrent.PublisherSource.Subscription)

Example 2 with CHUNKED

use of io.servicetalk.http.api.HttpHeaderValues.CHUNKED in project servicetalk by apple.

the class DefaultContainerResponseWriter method sendResponse.

private void sendResponse(final long contentLength, @Nullable final Publisher<Buffer> content, final ContainerResponse containerResponse) {
    final HttpResponseStatus status = getStatus(containerResponse);
    final StreamingHttpResponse response;
    if (content != null && !isHeadRequest()) {
        final HttpExecutionStrategy executionStrategy = getResponseExecutionStrategy(request);
        // TODO(scott): use request factory methods that accept a payload body to avoid overhead of payloadBody.
        final Publisher<Buffer> payloadBody = (executionStrategy != null && executionStrategy.isSendOffloaded() ? content.subscribeOn(serviceCtx.executionContext().executor(), IoThreadFactory.IoThread::currentThreadIsIoThread) : content).beforeCancel(// Cleanup internal state if server cancels response body
        this::cancelResponse);
        response = responseFactory.newResponse(status).version(protocolVersion).payloadBody(payloadBody);
    } else {
        response = responseFactory.newResponse(status).version(protocolVersion);
    }
    final HttpHeaders headers = response.headers();
    // If we use HTTP/2 protocol all headers MUST be in lower case
    final boolean isH2 = response.version().major() == 2;
    containerResponse.getHeaders().forEach((k, vs) -> vs.forEach(v -> {
        headers.add(isH2 ? k.toLowerCase() : k, v == null ? emptyAsciiString() : asCharSequence(v));
    }));
    if (!headers.contains(CONTENT_LENGTH)) {
        if (contentLength == UNKNOWN_RESPONSE_LENGTH) {
            // We can omit Transfer-Encoding for HEAD per https://tools.ietf.org/html/rfc7231#section-4.3.2
            if (!isHeadRequest() && !HTTP_1_0.equals(protocolVersion)) {
                headers.set(TRANSFER_ENCODING, CHUNKED);
            }
        } else {
            headers.set(CONTENT_LENGTH, contentLength == 0 ? ZERO : Long.toString(contentLength));
            headers.removeIgnoreCase(TRANSFER_ENCODING, CHUNKED);
        }
    }
    responseSubscriber.onSuccess(response);
}
Also used : Buffer(io.servicetalk.buffer.api.Buffer) AtomicIntegerFieldUpdater(java.util.concurrent.atomic.AtomicIntegerFieldUpdater) StreamingHttpResponse(io.servicetalk.http.api.StreamingHttpResponse) Publisher(io.servicetalk.concurrent.api.Publisher) Subscriber(io.servicetalk.concurrent.SingleSource.Subscriber) LoggerFactory(org.slf4j.LoggerFactory) HttpHeaders(io.servicetalk.http.api.HttpHeaders) Cancellable(io.servicetalk.concurrent.Cancellable) RequestProperties.getResponseExecutionStrategy(io.servicetalk.http.router.jersey.internal.RequestProperties.getResponseExecutionStrategy) ConnectableBufferOutputStream(io.servicetalk.concurrent.api.internal.ConnectableBufferOutputStream) RequestProperties.getResponseBufferPublisher(io.servicetalk.http.router.jersey.internal.RequestProperties.getResponseBufferPublisher) ContainerException(org.glassfish.jersey.server.ContainerException) ZERO(io.servicetalk.http.api.HttpHeaderValues.ZERO) HttpServiceContext(io.servicetalk.http.api.HttpServiceContext) IoThreadFactory(io.servicetalk.transport.api.IoThreadFactory) RequestProperties.getRequestCancellable(io.servicetalk.http.router.jersey.internal.RequestProperties.getRequestCancellable) Collectors.toMap(java.util.stream.Collectors.toMap) Map(java.util.Map) Objects.requireNonNull(java.util.Objects.requireNonNull) HttpExecutionStrategy(io.servicetalk.http.api.HttpExecutionStrategy) HttpProtocolVersion(io.servicetalk.http.api.HttpProtocolVersion) CharSequences.emptyAsciiString(io.servicetalk.buffer.api.CharSequences.emptyAsciiString) AtomicIntegerFieldUpdater.newUpdater(java.util.concurrent.atomic.AtomicIntegerFieldUpdater.newUpdater) Status(javax.ws.rs.core.Response.Status) ContainerResponseWriter(org.glassfish.jersey.server.spi.ContainerResponseWriter) Nullable(javax.annotation.Nullable) CHUNKED(io.servicetalk.http.api.HttpHeaderValues.CHUNKED) OutputStream(java.io.OutputStream) Logger(org.slf4j.Logger) TRANSFER_ENCODING(io.servicetalk.http.api.HttpHeaderNames.TRANSFER_ENCODING) IOException(java.io.IOException) ContainerRequest(org.glassfish.jersey.server.ContainerRequest) CONTENT_LENGTH(io.servicetalk.http.api.HttpHeaderNames.CONTENT_LENGTH) TimeUnit(java.util.concurrent.TimeUnit) StatusType(javax.ws.rs.core.Response.StatusType) ContainerResponse(org.glassfish.jersey.server.ContainerResponse) HEAD(javax.ws.rs.HttpMethod.HEAD) Buffer(io.servicetalk.buffer.api.Buffer) StreamingHttpResponseFactory(io.servicetalk.http.api.StreamingHttpResponseFactory) Function.identity(java.util.function.Function.identity) Collections.unmodifiableMap(java.util.Collections.unmodifiableMap) Arrays.stream(java.util.Arrays.stream) HttpResponseStatus(io.servicetalk.http.api.HttpResponseStatus) System.arraycopy(java.lang.System.arraycopy) HTTP_1_0(io.servicetalk.http.api.HttpProtocolVersion.HTTP_1_0) HttpHeaders(io.servicetalk.http.api.HttpHeaders) HttpResponseStatus(io.servicetalk.http.api.HttpResponseStatus) IoThreadFactory(io.servicetalk.transport.api.IoThreadFactory) HttpExecutionStrategy(io.servicetalk.http.api.HttpExecutionStrategy) StreamingHttpResponse(io.servicetalk.http.api.StreamingHttpResponse)

Example 3 with CHUNKED

use of io.servicetalk.http.api.HttpHeaderValues.CHUNKED in project servicetalk by apple.

the class AbstractEchoServerBasedHttpRequesterTest method makeRequestValidateResponseAndClose.

static void makeRequestValidateResponseAndClose(StreamingHttpRequester requester) throws ExecutionException, InterruptedException {
    try {
        StreamingHttpRequest request = requester.get("/request?foo=bar&foo=baz").payloadBody(from(DEFAULT_ALLOCATOR.fromAscii("Testing123")));
        request.headers().set(HOST, "mock.servicetalk.io");
        StreamingHttpResponse resp = awaitIndefinitelyNonNull(requester.request(request).retryWhen(retryWithExponentialBackoffFullJitter(10, t -> true, ofMillis(100), ofDays(10), CTX.executor())));
        assertThat(resp.status(), equalTo(OK));
        Single<String> respBody = resp.payloadBody().collect(StringBuilder::new, (sb, buf) -> {
            sb.append(buf.toString(UTF_8));
            return sb;
        }).map(StringBuilder::toString);
        HttpHeaders headers = resp.headers();
        assertThat(headers.get("test-req-method"), hasToString(GET.toString()));
        assertThat(headers.get("test-req-target"), hasToString("/request?foo=bar&foo=baz"));
        assertThat(headers.get("test-req-header-host"), hasToString("mock.servicetalk.io"));
        assertThat(headers.get("test-req-header-transfer-encoding"), equalTo(CHUNKED));
        assertThat(respBody.toFuture().get(), equalTo("Testing123"));
    } finally {
        requester.closeAsync().toFuture().get();
    }
}
Also used : Matchers.hasToString(org.hamcrest.Matchers.hasToString) StreamingHttpResponse(io.servicetalk.http.api.StreamingHttpResponse) DEFAULT_ALLOCATOR(io.servicetalk.buffer.netty.BufferAllocators.DEFAULT_ALLOCATOR) IsEqual.equalTo(org.hamcrest.core.IsEqual.equalTo) HttpHeaders(io.servicetalk.http.api.HttpHeaders) StreamingHttpRequester(io.servicetalk.http.api.StreamingHttpRequester) HttpServers.forAddress(io.servicetalk.http.netty.HttpServers.forAddress) AfterAll(org.junit.jupiter.api.AfterAll) HttpServiceContext(io.servicetalk.http.api.HttpServiceContext) TestInstance(org.junit.jupiter.api.TestInstance) BeforeAll(org.junit.jupiter.api.BeforeAll) RegisterExtension(org.junit.jupiter.api.extension.RegisterExtension) Single.succeeded(io.servicetalk.concurrent.api.Single.succeeded) HOST(io.servicetalk.http.api.HttpHeaderNames.HOST) BlockingTestUtils.awaitIndefinitelyNonNull(io.servicetalk.concurrent.api.BlockingTestUtils.awaitIndefinitelyNonNull) StreamingHttpRequest(io.servicetalk.http.api.StreamingHttpRequest) MatcherAssert.assertThat(org.hamcrest.MatcherAssert.assertThat) Publisher.from(io.servicetalk.concurrent.api.Publisher.from) CHUNKED(io.servicetalk.http.api.HttpHeaderValues.CHUNKED) Duration.ofDays(java.time.Duration.ofDays) AddressUtils.localAddress(io.servicetalk.transport.netty.internal.AddressUtils.localAddress) ServerContext(io.servicetalk.transport.api.ServerContext) RetryStrategies.retryWithExponentialBackoffFullJitter(io.servicetalk.concurrent.api.RetryStrategies.retryWithExponentialBackoffFullJitter) UTF_8(java.nio.charset.StandardCharsets.UTF_8) Single(io.servicetalk.concurrent.api.Single) ExecutionContextExtension(io.servicetalk.transport.netty.internal.ExecutionContextExtension) OK(io.servicetalk.http.api.HttpResponseStatus.OK) GET(io.servicetalk.http.api.HttpRequestMethod.GET) ExecutionException(java.util.concurrent.ExecutionException) StreamingHttpService(io.servicetalk.http.api.StreamingHttpService) StreamingHttpResponseFactory(io.servicetalk.http.api.StreamingHttpResponseFactory) Duration.ofMillis(java.time.Duration.ofMillis) HttpExecutionStrategies.offloadNever(io.servicetalk.http.api.HttpExecutionStrategies.offloadNever) HttpHeaders(io.servicetalk.http.api.HttpHeaders) StreamingHttpRequest(io.servicetalk.http.api.StreamingHttpRequest) Matchers.hasToString(org.hamcrest.Matchers.hasToString) StreamingHttpResponse(io.servicetalk.http.api.StreamingHttpResponse)

Aggregations

CHUNKED (io.servicetalk.http.api.HttpHeaderValues.CHUNKED)3 Buffer (io.servicetalk.buffer.api.Buffer)2 Subscriber (io.servicetalk.concurrent.SingleSource.Subscriber)2 Publisher (io.servicetalk.concurrent.api.Publisher)2 Single (io.servicetalk.concurrent.api.Single)2 TRANSFER_ENCODING (io.servicetalk.http.api.HttpHeaderNames.TRANSFER_ENCODING)2 HttpHeaders (io.servicetalk.http.api.HttpHeaders)2 OK (io.servicetalk.http.api.HttpResponseStatus.OK)2 HttpServiceContext (io.servicetalk.http.api.HttpServiceContext)2 StreamingHttpResponse (io.servicetalk.http.api.StreamingHttpResponse)2 StreamingHttpResponseFactory (io.servicetalk.http.api.StreamingHttpResponseFactory)2 IOException (java.io.IOException)2 Objects.requireNonNull (java.util.Objects.requireNonNull)2 CharSequences.emptyAsciiString (io.servicetalk.buffer.api.CharSequences.emptyAsciiString)1 DEFAULT_ALLOCATOR (io.servicetalk.buffer.netty.BufferAllocators.DEFAULT_ALLOCATOR)1 Cancellable (io.servicetalk.concurrent.Cancellable)1 Processor (io.servicetalk.concurrent.CompletableSource.Processor)1 Subscription (io.servicetalk.concurrent.PublisherSource.Subscription)1 BlockingTestUtils.awaitIndefinitelyNonNull (io.servicetalk.concurrent.api.BlockingTestUtils.awaitIndefinitelyNonNull)1 Completable (io.servicetalk.concurrent.api.Completable)1