001/*
002 *   Copyright 2024 Vonage
003 *
004 *   Licensed under the Apache License, Version 2.0 (the "License");
005 *   you may not use this file except in compliance with the License.
006 *   You may obtain a copy of the License at
007 *
008 *        http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *   Unless required by applicable law or agreed to in writing, software
011 *   distributed under the License is distributed on an "AS IS" BASIS,
012 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *   See the License for the specific language governing permissions and
014 *   limitations under the License.
015 */
016package com.vonage.client;
017
018import com.vonage.client.auth.ApiKeyAuthMethod;
019import com.vonage.client.auth.AuthCollection;
020import com.vonage.client.auth.AuthMethod;
021import com.vonage.client.auth.JWTAuthMethod;
022import org.apache.http.HttpHost;
023import org.apache.http.client.HttpClient;
024import org.apache.http.client.config.RequestConfig;
025import org.apache.http.config.ConnectionConfig;
026import org.apache.http.config.SocketConfig;
027import org.apache.http.impl.client.CloseableHttpClient;
028import org.apache.http.impl.client.HttpClientBuilder;
029import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
030import java.net.URI;
031import java.nio.charset.StandardCharsets;
032import java.util.UUID;
033
034/**
035 * Internal class that holds available authentication methods and a shared HttpClient.
036 */
037public class HttpWrapper {
038    private static final String
039            CLIENT_NAME = "vonage-java-sdk",
040            CLIENT_VERSION = "8.15.1",
041            JAVA_VERSION = System.getProperty("java.version"),
042            USER_AGENT = String.format("%s/%s java/%s", CLIENT_NAME, CLIENT_VERSION, JAVA_VERSION);
043
044    private AuthCollection authCollection;
045    private CloseableHttpClient httpClient;
046    private HttpConfig httpConfig;
047
048    public HttpWrapper(HttpConfig httpConfig, AuthCollection authCollection) {
049        this.authCollection = authCollection;
050        this.httpConfig = httpConfig;
051    }
052
053    public HttpWrapper(AuthCollection authCollection) {
054        this(HttpConfig.builder().build(), authCollection);
055    }
056
057    public HttpWrapper(AuthMethod... authMethods) {
058        this(HttpConfig.builder().build(), authMethods);
059    }
060
061    public HttpWrapper(HttpConfig httpConfig, AuthMethod... authMethods) {
062        this(httpConfig, new AuthCollection(authMethods));
063    }
064
065    /**
066     * Gets the underlying {@link HttpClient} instance used by the SDK.
067     *
068     * @return The Apache HTTP client instance.
069     */
070    public CloseableHttpClient getHttpClient() {
071        if (httpClient == null) {
072            httpClient = createHttpClient();
073        }
074        return httpClient;
075    }
076
077    /**
078     * Returns the application ID if it was set when creating the client.
079     *
080     * @return The application ID, or {@code null} if unavailable.
081     * @since 8.9.0
082     */
083    public UUID getApplicationId() {
084        try {
085            return UUID.fromString(getAuthCollection().getAuth(JWTAuthMethod.class).getApplicationId());
086        }
087        catch (RuntimeException ex) {
088            return null;
089        }
090    }
091
092    /**
093     * Returns the API key if it was set when creating the client.
094     *
095     * @return The API key, or {@code null} if unavailable.
096     * @since 8.9.0
097     */
098    public String getApiKey() {
099        try {
100            return getAuthCollection().getAuth(ApiKeyAuthMethod.class).getApiKey();
101        }
102        catch (RuntimeException ex) {
103            return null;
104        }
105    }
106
107    @Deprecated
108    public void setHttpClient(HttpClient httpClient) {
109        this.httpClient = (CloseableHttpClient) httpClient;
110    }
111
112    @Deprecated
113    public void setHttpConfig(HttpConfig httpConfig) {
114        this.httpConfig = httpConfig;
115    }
116
117    /**
118     * Gets the authentication settings used by the client.
119     *
120     * @return The available authentication methods object.
121     */
122    public AuthCollection getAuthCollection() {
123        return authCollection;
124    }
125
126    @Deprecated
127    public void setAuthCollection(AuthCollection authCollection) {
128        this.authCollection = authCollection;
129    }
130
131    protected CloseableHttpClient createHttpClient() {
132        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
133        connectionManager.setDefaultMaxPerRoute(200);
134        connectionManager.setMaxTotal(200);
135        connectionManager.setDefaultConnectionConfig(
136            ConnectionConfig.custom().setCharset(StandardCharsets.UTF_8).build()
137        );
138        connectionManager.setDefaultSocketConfig(SocketConfig.custom().setTcpNoDelay(true).build());
139
140        // Need to work out a good value for the following:
141        // threadSafeClientConnManager.setValidateAfterInactivity();
142        RequestConfig requestConfig = RequestConfig.custom()
143                .setConnectTimeout(httpConfig.getTimeoutMillis())
144                .setConnectionRequestTimeout(httpConfig.getTimeoutMillis())
145                .setSocketTimeout(httpConfig.getTimeoutMillis())
146                .build();
147
148        HttpClientBuilder clientBuilder = HttpClientBuilder.create()
149                .setConnectionManager(connectionManager)
150                .setUserAgent(getUserAgent())
151                .setDefaultRequestConfig(requestConfig)
152                .useSystemProperties().disableRedirectHandling();
153
154        URI proxy = httpConfig.getProxy();
155        if (proxy != null) {
156            clientBuilder.setProxy(new HttpHost(proxy.getHost(), proxy.getPort(), proxy.getScheme()));
157        }
158
159        return clientBuilder.build();
160    }
161
162    /**
163     * Gets the HTTP configuration settings for the client.
164     *
165     * @return The request configuration settings object.
166     */
167    public HttpConfig getHttpConfig() {
168        return httpConfig;
169    }
170
171    /**
172     * Gets the {@code User-Agent} header to be used in HTTP requests.
173     * This includes the Java runtime and SDK version, as well as the custom user agent string, if present.
174     *
175     * @return The user agent string.
176     */
177    public String getUserAgent() {
178        String ua = USER_AGENT, custom = httpConfig.getCustomUserAgent();
179        if (custom != null) {
180            ua += " " + httpConfig.getCustomUserAgent();
181        }
182        return ua;
183    }
184
185    /**
186     * Gets the SDK version.
187     *
188     * @return The SDK version as a string.
189     * @since 8.11.0
190     */
191    public String getClientVersion() {
192        return CLIENT_VERSION;
193    }
194}