All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.helidon.webclient.http2.Http2ClientResponseImpl Maven / Gradle / Ivy

There is a newer version: 4.1.4
Show newest version
/*
 * Copyright (c) 2022, 2023 Oracle and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.helidon.webclient.http2;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;

import io.helidon.common.buffers.BufferData;
import io.helidon.http.ClientRequestHeaders;
import io.helidon.http.ClientResponseHeaders;
import io.helidon.http.ClientResponseTrailers;
import io.helidon.http.Status;
import io.helidon.http.media.MediaContext;
import io.helidon.http.media.ReadableEntity;
import io.helidon.webclient.api.ClientResponseEntity;
import io.helidon.webclient.api.ClientUri;
import io.helidon.webclient.api.HttpClientConfig;
import io.helidon.webclient.api.ReleasableResource;

class Http2ClientResponseImpl implements Http2ClientResponse {
    private final HttpClientConfig httpClientConfig;
    private final Status responseStatus;
    private final ClientRequestHeaders requestHeaders;
    private final ClientResponseHeaders responseHeaders;
    private final ReleasableResource stream;
    private final CompletableFuture complete;
    private final Runnable closeResponseRunnable;
    private final CompletableFuture responseTrailers;
    private final InputStream inputStream;
    private final MediaContext mediaContext;
    private final ClientUri lastEndpointUri;
    private final AtomicBoolean closed = new AtomicBoolean(false);
    private boolean entityRequested;

    Http2ClientResponseImpl(HttpClientConfig httpClientConfig,
                            Status status,
                            ClientRequestHeaders requestHeaders,
                            ClientResponseHeaders responseHeaders,
                            CompletableFuture responseTrailers,
                            InputStream inputStream, // input stream is nullable - no response entity
                            MediaContext mediaContext,
                            ClientUri lastEndpointUri,
                            ReleasableResource stream,
                            CompletableFuture complete,
                            Runnable closeResponseRunnable) {
        this.httpClientConfig = httpClientConfig;
        this.responseStatus = status;
        this.requestHeaders = requestHeaders;
        this.responseHeaders = responseHeaders;
        this.responseTrailers = responseTrailers;
        this.inputStream = inputStream;
        this.mediaContext = mediaContext;
        this.lastEndpointUri = lastEndpointUri;
        this.stream = stream;
        this.complete = complete;
        this.closeResponseRunnable = closeResponseRunnable;
    }

    @Override
    public Status status() {
        return responseStatus;
    }

    @Override
    public ClientResponseHeaders headers() {
        return responseHeaders;
    }

    @Override
    public ClientResponseTrailers trailers() {
        // Block until trailers arrive
        Duration timeout = httpClientConfig.readTimeout()
                .orElseGet(() -> httpClientConfig.socketOptions().readTimeout());

        if (!this.entityRequested) {
            throw new IllegalStateException("Trailers requested before reading entity.");
        }

        try {
            return ClientResponseTrailers.create(this.responseTrailers.get(timeout.toMillis(), TimeUnit.MILLISECONDS));
        } catch (TimeoutException e) {
            throw new IllegalStateException("Timeout " + timeout + " reached while waiting for trailers.", e);
        } catch (InterruptedException e) {
            throw new IllegalStateException("Interrupted while waiting for trailers.", e);
        } catch (ExecutionException e) {
            if (e.getCause() instanceof IllegalStateException ise) {
                throw ise;
            } else {
                throw new IllegalStateException(e.getCause());
            }
        }
    }

    @Override
    public ReadableEntity entity() {
        this.entityRequested = true;

        if (inputStream == null) {
            return ClientResponseEntity.empty();
        }

        return ClientResponseEntity.create(
                this::readBytes,
                this::close,
                requestHeaders,
                responseHeaders,
                mediaContext
        );
    }

    Http2ClientStream stream() {
        return (Http2ClientStream) stream;
    }

    private BufferData readBytes(int estimate) {
        try {
            // Empty buffer is considered as a fully consumed entity
            // so estimate can't be less than 1
            byte[] buffer = new byte[estimate > 0 ? estimate : 16];
            int read = inputStream.read(buffer);
            if (read < 1) {
                return BufferData.empty();
            }
            return BufferData.create(buffer, 0, read);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    @Override
    public ClientUri lastEndpointUri() {
        return lastEndpointUri;
    }

    @Override
    public void close() {
        if (!closed.getAndSet(true)) {
            complete.complete(null);
            closeResponseRunnable.run();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy