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

io.helidon.webclient.http2.Http2ClientRequestImpl 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.time.Duration;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

import io.helidon.http.Method;
import io.helidon.webclient.api.ClientRequestBase;
import io.helidon.webclient.api.ClientUri;
import io.helidon.webclient.api.FullClientRequest;
import io.helidon.webclient.api.WebClientServiceRequest;
import io.helidon.webclient.api.WebClientServiceResponse;

class Http2ClientRequestImpl extends ClientRequestBase
        implements Http2ClientRequest, Http2StreamConfig, FullClientRequest {

    private final Http2ClientImpl http2Client;
    private int priority = 16;
    private boolean priorKnowledge;
    private int requestPrefetch = 0;
    private Duration flowControlTimeout = Duration.ofMillis(100);
    private boolean outputStreamRedirect = false;

    Http2ClientRequestImpl(Http2ClientImpl http2Client,
                           Method method,
                           ClientUri clientUri,
                           Map properties) {
        super(http2Client.clientConfig(),
                http2Client.webClient().cookieManager(),
                Http2Client.PROTOCOL_ID,
                method,
                clientUri,
                properties);

        this.http2Client = http2Client;
        Http2ClientProtocolConfig protocolConfig = http2Client.protocolConfig();
        this.priorKnowledge = protocolConfig.priorKnowledge();
    }

    Http2ClientRequestImpl(Http2ClientRequestImpl request,
                           Method method,
                           ClientUri clientUri,
                           Map properties) {
        this(request.http2Client, method, clientUri, properties);

        followRedirects(request.followRedirects());
        maxRedirects(request.maxRedirects());
        tls(request.tls());

        this.priority(request.priority);
        this.priorKnowledge(request.priorKnowledge);
        this.flowControlTimeout(request.flowControlTimeout);
        this.requestPrefetch(request.requestPrefetch);
        this.readTimeout(request.readTimeout());
        this.outputStreamRedirect(request.outputStreamRedirect);
    }

    @Override
    public Http2ClientRequest priority(int priority) {
        if (priority < 1 || priority > 256) {
            throw new IllegalArgumentException("Priority must be between 1 and 256 (inclusive), but is " + priority);
        }
        this.priority = priority;
        return this;
    }

    @Override
    public Http2ClientRequest priorKnowledge(boolean priorKnowledge) {
        this.priorKnowledge = priorKnowledge;
        return this;
    }

    @Override
    public Http2ClientRequest requestPrefetch(int requestPrefetch) {
        this.requestPrefetch = requestPrefetch;
        return this;
    }

    @Override
    public Http2ClientRequest flowControlTimeout(Duration timeout) {
        this.flowControlTimeout = timeout;
        return this;
    }

    @Override
    public Http2ClientResponse doSubmit(Object entity) {
        if (followRedirects()) {
            return RedirectionProcessor.invokeWithFollowRedirects(this, 0, entity);
        }
        return invokeEntity(entity);
    }

    @Override
    public Http2ClientResponse doOutputStream(OutputStreamHandler streamHandler) {
        CompletableFuture whenSent = new CompletableFuture<>();
        CompletableFuture whenComplete = new CompletableFuture<>();
        Http2CallChainBase callChain = new Http2CallOutputStreamChain(http2Client,
                                                                      this,
                                                                      whenSent,
                                                                      whenComplete,
                                                                      streamHandler);

        return invokeWithServices(callChain, whenSent, whenComplete);
    }

    @Override
    public boolean priorKnowledge() {
        return priorKnowledge;
    }

    @Override
    public int priority() {
        return priority;
    }

    // this is currently not used - if it is to be used, it must be per stream configuration, not connection wide
    int requestPrefetch() {
        return requestPrefetch;
    }

    // this is currently not used - if it is to be used, it must be per stream configuration, not connection wide
    Duration flowControlTimeout() {
        return flowControlTimeout;
    }

    /**
     * Whether this request is part of output stream redirection
     * Default is {@code false}.
     *
     * @param outputStreamRedirect whether this request is part of output stream redirection
     * @return updated request
     */
    Http2ClientRequestImpl outputStreamRedirect(boolean outputStreamRedirect) {
        this.outputStreamRedirect = outputStreamRedirect;
        return this;
    }

    boolean outputStreamRedirect() {
        return outputStreamRedirect;
    }

    Http2ClientResponseImpl invokeEntity(Object entity) {
        CompletableFuture whenSent = new CompletableFuture<>();
        CompletableFuture whenComplete = new CompletableFuture<>();
        Http2CallChainBase httpCall = new Http2CallEntityChain(http2Client,
                                                               this,
                                                               whenSent,
                                                               whenComplete,
                                                               entity);

        return invokeWithServices(httpCall, whenSent, whenComplete);
    }

    private Http2ClientResponseImpl invokeWithServices(Http2CallChainBase callChain,
                                                       CompletableFuture whenSent,
                                                       CompletableFuture whenComplete) {

        // will create a copy, so we could invoke this method multiple times
        ClientUri resolvedUri = resolvedUri();

        WebClientServiceResponse serviceResponse = invokeServices(callChain, whenSent, whenComplete, resolvedUri);

        CompletableFuture complete = new CompletableFuture<>();
        complete.thenAccept(ignored -> serviceResponse.whenComplete().complete(serviceResponse))
                .exceptionally(throwable -> {
                    serviceResponse.whenComplete().completeExceptionally(throwable);
                    return null;
                });

        // if this was an HTTP/1.1 response, do something different (just re-use response)
        return new Http2ClientResponseImpl(clientConfig(),
                                           serviceResponse.status(),
                                           callChain.requestHeaders(),
                                           serviceResponse.headers(),
                                           serviceResponse.trailers(),
                                           serviceResponse.inputStream().orElse(null),
                                           mediaContext(),
                                           resolvedUri,
                                           serviceResponse.connection(),
                                           complete,
                                           callChain::closeResponse);

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy