aboutsummaryrefslogtreecommitdiffstats
path: root/http-utils/src/main/java/ai/vespa/util/http/hc5/VespaAsyncHttpClientBuilder.java
blob: 91810b5077846ceddbc4e3a95bbe19909c7aca7e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package ai.vespa.util.http.hc5;

import com.yahoo.security.tls.MixedMode;
import com.yahoo.security.tls.TlsContext;
import com.yahoo.security.tls.TransportSecurityUtils;
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLParameters;

/**
 * Async http client builder for internal Vespa communications over http/https.
 * Configures Vespa mTLS and handles TLS mixed mode automatically.
 * Client should only be used for requests to Vespa services.
 *
 * Caveats:
 * - custom connection manager must be configured through {@link #create(AsyncConnectionManagerFactory)}.
 *
 * @author bjorncs
 */
public class VespaAsyncHttpClientBuilder {

    public interface AsyncConnectionManagerFactory {
        AsyncClientConnectionManager create(TlsStrategy tlsStrategy);
    }

    public static HttpAsyncClientBuilder create() {
        return create(
                tlsStrategy -> PoolingAsyncClientConnectionManagerBuilder.create()
                        .setTlsStrategy(tlsStrategy)
                        .build());
    }

    public static HttpAsyncClientBuilder create(AsyncConnectionManagerFactory factory) {
        return create(factory, new NoopHostnameVerifier());
    }

    public static HttpAsyncClientBuilder create(AsyncConnectionManagerFactory factory, HostnameVerifier hostnameVerifier) {
        HttpAsyncClientBuilder clientBuilder = HttpAsyncClientBuilder.create();
        TlsContext vespaTlsContext = TransportSecurityUtils.getSystemTlsContext().orElse(null);
        TlsStrategy tlsStrategy;
        if (vespaTlsContext != null) {
            SSLParameters vespaTlsParameters = vespaTlsContext.parameters();
            tlsStrategy = ClientTlsStrategyBuilder.create()
                    .setHostnameVerifier(hostnameVerifier)
                    .setSslContext(vespaTlsContext.context())
                    .setTlsVersions(vespaTlsParameters.getProtocols())
                    .setCiphers(vespaTlsParameters.getCipherSuites())
                    .build();
            if (TransportSecurityUtils.getInsecureMixedMode() != MixedMode.PLAINTEXT_CLIENT_MIXED_SERVER) {
                clientBuilder.setRoutePlanner(new HttpToHttpsRoutePlanner());
            }
        } else {
            tlsStrategy = ClientTlsStrategyBuilder.create().build();
        }
        clientBuilder.disableConnectionState(); // Share connections between subsequent requests
        clientBuilder.disableCookieManagement();
        clientBuilder.disableAuthCaching();
        clientBuilder.disableRedirectHandling();
        clientBuilder.setConnectionManager(factory.create(tlsStrategy));
        clientBuilder.setConnectionManagerShared(false);
        return clientBuilder;
    }

}