1 /**
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  * SPDX-License-Identifier: Apache-2.0.
4  */
5 package software.amazon.awssdk.crt.http;
6 
7 import java.net.URI;
8 import software.amazon.awssdk.crt.io.ClientBootstrap;
9 import software.amazon.awssdk.crt.io.SocketOptions;
10 import software.amazon.awssdk.crt.io.TlsConnectionOptions;
11 import software.amazon.awssdk.crt.io.TlsContext;
12 
13 /**
14  * Contains all the configuration options for a HttpConnectionPoolManager instance
15  */
16 public class HttpClientConnectionManagerOptions {
17     public static final int DEFAULT_MAX_BUFFER_SIZE = 16 * 1024;
18     public static final long DEFAULT_MAX_WINDOW_SIZE = Integer.MAX_VALUE;
19     public static final int DEFAULT_MAX_CONNECTIONS = 2;
20 
21     private ClientBootstrap clientBootstrap;
22     private SocketOptions socketOptions;
23     private TlsContext tlsContext;
24     private TlsConnectionOptions tlsConnectionOptions;
25     private long windowSize = DEFAULT_MAX_WINDOW_SIZE;
26     private int bufferSize = DEFAULT_MAX_BUFFER_SIZE;
27     private URI uri;
28     private int port = -1;
29     private int maxConnections = DEFAULT_MAX_CONNECTIONS;
30     private HttpProxyOptions proxyOptions;
31     private HttpProxyEnvironmentVariableSetting httpProxyEnvironmentVariableSetting;
32     private boolean manualWindowManagement = false;
33     private HttpMonitoringOptions monitoringOptions;
34     private long maxConnectionIdleInMilliseconds = 0;
35     private HttpVersion expectedHttpVersion = HttpVersion.HTTP_1_1;
36 
37     private static final String HTTP = "http";
38     private static final String HTTPS = "https";
39 
40     /**
41      * Default constructor
42      */
HttpClientConnectionManagerOptions()43     public HttpClientConnectionManagerOptions() {
44     }
45 
46     /**
47      * Sets the client bootstrap instance to use to create the pool's connections
48      * @param clientBootstrap ClientBootstrap to use
49      * @return this
50      */
withClientBootstrap(ClientBootstrap clientBootstrap)51     public HttpClientConnectionManagerOptions withClientBootstrap(ClientBootstrap clientBootstrap) {
52         this.clientBootstrap = clientBootstrap;
53         return this;
54     }
55 
56     /**
57      * Gets the client bootstrap instance to use to create the pool's connections
58      * @return ClientBootstrap used by this connection manager
59      */
getClientBootstrap()60     public ClientBootstrap getClientBootstrap() { return clientBootstrap; }
61 
62     /**
63      * Sets the socket options to use for connections in the connection pool
64      * @param socketOptions The socket options to use for all connections in the manager
65      * @return this
66      */
withSocketOptions(SocketOptions socketOptions)67     public HttpClientConnectionManagerOptions withSocketOptions(SocketOptions socketOptions) {
68         this.socketOptions = socketOptions;
69         return this;
70     }
71 
72     /**
73      * @return the socket options to use for connections in the connection pool
74      */
getSocketOptions()75     public SocketOptions getSocketOptions() { return socketOptions; }
76 
77     /**
78      * Sets the tls context to use for connections in the connection pool
79      * @param tlsContext The TlsContext to use
80      * @return this
81      */
withTlsContext(TlsContext tlsContext)82     public HttpClientConnectionManagerOptions withTlsContext(TlsContext tlsContext) {
83         this.tlsContext = tlsContext;
84         return this;
85     }
86 
87     /**
88      * @return the tls context used by connections in the connection pool
89      */
getTlsContext()90     public TlsContext getTlsContext() { return tlsContext; }
91 
92     /**
93      * Sets the connection-specific TLS options to use for connections in the connection pool.
94      * Either TLS context or TLS connection options will be enough to set up TLS connection.
95      * If both set, an exception will be raised.
96      * @param tlsConnectionOptions The TlsConnectionOptions to use
97      * @return this
98      */
withTlsConnectionOptions(TlsConnectionOptions tlsConnectionOptions)99     public HttpClientConnectionManagerOptions withTlsConnectionOptions(TlsConnectionOptions tlsConnectionOptions) {
100         this.tlsConnectionOptions = tlsConnectionOptions;
101         return this;
102     }
103 
104     /**
105      * @return the tls context used by connections in the connection pool
106      */
getTlsConnectionOptions()107     public TlsConnectionOptions getTlsConnectionOptions() { return tlsConnectionOptions; }
108 
109     /**
110      * Sets the starting size of each HTTP stream's flow-control window.
111      * This is only used when "manual window management" is enabled.
112      *
113      * @param windowSize The initial window size for each HTTP stream
114      * @return this
115      * @see #withManualWindowManagement
116      */
withWindowSize(long windowSize)117     public HttpClientConnectionManagerOptions withWindowSize(long windowSize) {
118         this.windowSize = windowSize;
119         return this;
120     }
121 
122     /**
123      * @return The starting size of each HTTP stream's flow-control window.
124      */
getWindowSize()125     public long getWindowSize() { return windowSize; }
126 
127     /**
128      * @deprecated Sets the IO buffer size to use for connections in the connection pool
129      * @param bufferSize Size of I/O buffer per connection
130      * @return this
131      */
withBufferSize(int bufferSize)132     public HttpClientConnectionManagerOptions withBufferSize(int bufferSize) {
133         this.bufferSize = bufferSize;
134         return this;
135     }
136 
137     /**
138      * @deprecated
139      * @return the IO buffer size to use for connections in the connection pool
140      */
getBufferSize()141     public int getBufferSize() { return bufferSize; }
142 
143     /**
144      * Sets the URI to use for connections in the connection pool
145      * @param uri The endpoint URI to connect to
146      * @return this
147      */
withUri(URI uri)148     public HttpClientConnectionManagerOptions withUri(URI uri) {
149         this.uri = uri;
150         return this;
151     }
152 
153     /**
154      * @return the URI to use for connections in the connection pool
155      */
getUri()156     public URI getUri() { return uri; }
157 
158     /**
159      * Sets the port to connect to for connections in the connection pool.
160      * For 32bit values exceeding Integer.MAX_VALUE use two's complement
161      * (i.e. -1 == 0xFFFFFFFF).
162      * @param port The port to connect to
163      * @return this
164      */
withPort(int port)165     public HttpClientConnectionManagerOptions withPort(int port) {
166         this.port = port;
167         return this;
168     }
169 
170     /**
171      * @return the port to connect to for connections in the connection pool.
172      *         Returns -1 if none has been explicitly set.
173      *         Note that two's complement is used for 32bit values exceeding
174      *         Integer.MAX_VALUE (i.e. -1 == 0xFFFFFFFF).
175      */
getPort()176     public int getPort() { return port; }
177 
178     /**
179      * Sets the maximum number of connections allowed in the connection pool
180      * @param maxConnections maximum number of connections to pool
181      * @return this
182      */
withMaxConnections(int maxConnections)183     public HttpClientConnectionManagerOptions withMaxConnections(int maxConnections) {
184         this.maxConnections = maxConnections;
185         return this;
186     }
187 
188     /**
189      * @return the maximum number of connections allowed in the connection pool
190      */
getMaxConnections()191     public int getMaxConnections() { return maxConnections; }
192 
193     /**
194      * Sets the proxy options for connections in the connection pool
195      * @param proxyOptions HttpProxyOptions for this connection manager, or null to disable proxying
196      * @return this
197      */
withProxyOptions(HttpProxyOptions proxyOptions)198     public HttpClientConnectionManagerOptions withProxyOptions(HttpProxyOptions proxyOptions) {
199         this.proxyOptions = proxyOptions;
200         return this;
201     }
202 
203     /**
204      * @return the proxy options for connections in the connection pool
205      */
getProxyOptions()206     public HttpProxyOptions getProxyOptions() { return proxyOptions; }
207 
208     /**
209      * Optional.
210      * Sets how proxy is fetched from the environment.
211      * Reading proxy configuration from environment is disabled if this is NULL for backward compatibility.
212      * Only works when proxyOptions is not set. The proxy settings follow the following precedence
213      * 1. Configured Proxy Setting
214      * 2. Environment (if enabled)
215      * 3. No proxy
216      * @param httpProxyEnvironmentVariableSetting  for this connection manager
217      * @return this
218      */
withProxyEnvironmentVariableSetting( HttpProxyEnvironmentVariableSetting httpProxyEnvironmentVariableSetting)219     public HttpClientConnectionManagerOptions withProxyEnvironmentVariableSetting(
220             HttpProxyEnvironmentVariableSetting httpProxyEnvironmentVariableSetting) {
221         this.httpProxyEnvironmentVariableSetting = httpProxyEnvironmentVariableSetting;
222         return this;
223     }
224 
225     /**
226      * @return the proxy environment variable setting
227      */
getHttpProxyEnvironmentVariableSetting()228     public HttpProxyEnvironmentVariableSetting getHttpProxyEnvironmentVariableSetting() {
229         return httpProxyEnvironmentVariableSetting;
230     }
231 
232     /**
233      * @return true if manual window management is used, false otherwise
234      * @see #withManualWindowManagement
235      */
isManualWindowManagement()236     public boolean isManualWindowManagement() { return manualWindowManagement; }
237 
238     /**
239      * If set to true, then you must manage the read backpressure mechanism. You should
240      * only use this if you're allowing http response body data to escape the callbacks. E.g. you're
241      * putting the data into a queue for another thread to process and need to make sure the memory
242      * usage is bounded (e.g. reactive streams).
243      * <p>
244      * When enabled, each HttpStream has a flow-control window that shrinks as response body data is downloaded
245      * (headers do not affect the window). {@link #withWindowSize} determines the starting size of each
246      * HttpStream's window, in bytes. Data stops downloading whenever the window reaches zero.
247      * Increment the window to keep data flowing by calling {@link HttpStreamBase#incrementWindow},
248      * or by returning a size from {@link HttpStreamResponseHandler#onResponseBody}.
249      * Maintain a larger window to keep up a high download throughput,
250      * or use a smaller window to limit how much data could get buffered in memory.
251      *
252      * @param manualWindowManagement true to enable manual window management, false to use automatic window management
253      * @return this
254      */
withManualWindowManagement(boolean manualWindowManagement)255     public HttpClientConnectionManagerOptions withManualWindowManagement(boolean manualWindowManagement) {
256         this.manualWindowManagement = manualWindowManagement;
257         return this;
258     }
259 
260     /**
261      * Set the expected protocol version of the connection to be made, default is HTTP/1.1
262      *
263      * @param expectedHttpVersion The expected protocol version of the connection made
264      * @return this
265      */
withExpectedHttpVersion(HttpVersion expectedHttpVersion)266     public HttpClientConnectionManagerOptions withExpectedHttpVersion(HttpVersion expectedHttpVersion) {
267         this.expectedHttpVersion = expectedHttpVersion;
268         return this;
269     }
270 
271     /**
272      * @return Return the expected HTTP protocol version.
273      */
getExpectedHttpVersion()274     public HttpVersion getExpectedHttpVersion() {
275         return expectedHttpVersion;
276     }
277 
278     /**
279      * Sets maximum amount of time, in milliseconds, that the connection can be idle in the manager before
280      * getting culled by the manager
281      * @param maxConnectionIdleInMilliseconds How long to allow connections to be idle before reaping them
282      * @return this
283      */
withMaxConnectionIdleInMilliseconds(long maxConnectionIdleInMilliseconds)284     public HttpClientConnectionManagerOptions withMaxConnectionIdleInMilliseconds(long maxConnectionIdleInMilliseconds) {
285         this.maxConnectionIdleInMilliseconds = maxConnectionIdleInMilliseconds;
286         return this;
287     }
288 
289     /**
290      * @return How long to allow connections to be idle before reaping them
291      */
getMaxConnectionIdleInMilliseconds()292     public long getMaxConnectionIdleInMilliseconds() { return maxConnectionIdleInMilliseconds; }
293 
294     /**
295      * Sets the monitoring options for connections in the connection pool
296      * @param monitoringOptions Monitoring options for this connection manager, or null to disable monitoring
297      * @return this
298      */
withMonitoringOptions(HttpMonitoringOptions monitoringOptions)299     public HttpClientConnectionManagerOptions withMonitoringOptions(HttpMonitoringOptions monitoringOptions) {
300         this.monitoringOptions = monitoringOptions;
301         return this;
302     }
303 
304     /**
305      * @return the monitoring options for connections in the connection pool
306      */
getMonitoringOptions()307     public HttpMonitoringOptions getMonitoringOptions() { return monitoringOptions; }
308 
309     /**
310      * Validate the connection manager options are valid to use. Throw exceptions if not.
311      */
validateOptions()312     public void validateOptions() {
313         URI uri = this.getUri();
314         if (uri == null) {  throw new IllegalArgumentException("URI must not be null"); }
315         if (uri.getScheme() == null) { throw new IllegalArgumentException("URI does not have a Scheme"); }
316         if (!HTTP.equals(uri.getScheme()) && !HTTPS.equals(uri.getScheme())) { throw new IllegalArgumentException("URI has unknown Scheme"); }
317         if (uri.getHost() == null) { throw new IllegalArgumentException("URI does not have a Host name"); }
318 
319         if (clientBootstrap == null) {  throw new IllegalArgumentException("ClientBootstrap must not be null"); }
320 
321         if (socketOptions == null) { throw new IllegalArgumentException("SocketOptions must not be null"); }
322 
323         if(tlsContext!= null && tlsConnectionOptions != null) {
324             throw new IllegalArgumentException("Cannot set both TlsContext and TlsConnectionOptions.");
325         }
326         boolean useTls = HTTPS.equals(uri.getScheme());
327         boolean tlsSet = (tlsContext!= null || tlsConnectionOptions != null);
328         if (useTls && !tlsSet) { throw new IllegalArgumentException("TlsContext or TlsConnectionOptions must not be null if https is used"); }
329 
330         if (windowSize <= 0) { throw new  IllegalArgumentException("Window Size must be greater than zero."); }
331 
332         if (maxConnections <= 0) { throw new  IllegalArgumentException("Max Connections must be greater than zero."); }
333     }
334 }
335