• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */
15 
16 package software.amazon.awssdk.http.crt;
17 
18 import static software.amazon.awssdk.http.HttpMetric.HTTP_CLIENT_NAME;
19 import static software.amazon.awssdk.utils.Validate.paramNotNull;
20 
21 import java.time.Duration;
22 import java.util.concurrent.CompletableFuture;
23 import java.util.function.Consumer;
24 import software.amazon.awssdk.annotations.SdkPublicApi;
25 import software.amazon.awssdk.crt.http.HttpClientConnectionManager;
26 import software.amazon.awssdk.http.SdkHttpConfigurationOption;
27 import software.amazon.awssdk.http.async.AsyncExecuteRequest;
28 import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
29 import software.amazon.awssdk.http.crt.internal.AwsCrtClientBuilderBase;
30 import software.amazon.awssdk.http.crt.internal.CrtAsyncRequestContext;
31 import software.amazon.awssdk.http.crt.internal.CrtAsyncRequestExecutor;
32 import software.amazon.awssdk.utils.AttributeMap;
33 
34 /**
35  * An implementation of {@link SdkAsyncHttpClient} that uses the AWS Common Runtime (CRT) Http Client to communicate with
36  * Http Web Services. This client is asynchronous and uses non-blocking IO.
37  *
38  * <p>This can be created via {@link #builder()}</p>
39  * {@snippet :
40     SdkAsyncHttpClient client = AwsCrtAsyncHttpClient.builder()
41                                                 .maxConcurrency(100)
42                                                 .connectionTimeout(Duration.ofSeconds(1))
43                                                 .connectionMaxIdleTime(Duration.ofSeconds(5))
44                                                 .build();
45  * }
46  *
47  */
48 @SdkPublicApi
49 public final class AwsCrtAsyncHttpClient extends AwsCrtHttpClientBase implements SdkAsyncHttpClient {
50 
AwsCrtAsyncHttpClient(DefaultAsyncBuilder builder, AttributeMap config)51     private AwsCrtAsyncHttpClient(DefaultAsyncBuilder builder, AttributeMap config) {
52         super(builder, config);
53     }
54 
builder()55     public static AwsCrtAsyncHttpClient.Builder builder() {
56         return new DefaultAsyncBuilder();
57     }
58 
59     /**
60      * Create a {@link AwsCrtAsyncHttpClient} client with the default configuration
61      *
62      * @return an {@link SdkAsyncHttpClient}
63      */
create()64     public static SdkAsyncHttpClient create() {
65         return new DefaultAsyncBuilder().build();
66     }
67 
68     @Override
clientName()69     public String clientName() {
70         return super.clientName();
71     }
72 
73     @Override
execute(AsyncExecuteRequest asyncRequest)74     public CompletableFuture<Void> execute(AsyncExecuteRequest asyncRequest) {
75 
76         paramNotNull(asyncRequest, "asyncRequest");
77         paramNotNull(asyncRequest.request(), "SdkHttpRequest");
78         paramNotNull(asyncRequest.requestContentPublisher(), "RequestContentPublisher");
79         paramNotNull(asyncRequest.responseHandler(), "ResponseHandler");
80 
81         asyncRequest.metricCollector()
82                     .ifPresent(metricCollector -> metricCollector.reportMetric(HTTP_CLIENT_NAME, clientName()));
83 
84         /*
85          * See the note on getOrCreateConnectionPool()
86          *
87          * In particular, this returns a ref-counted object and calling getOrCreateConnectionPool
88          * increments the ref count by one.  We add a try-with-resources to release our ref
89          * once we have successfully submitted a request.  In this way, we avoid a race condition
90          * when close/shutdown is called from another thread while this function is executing (ie.
91          * we have a pool and no one can destroy it underneath us until we've finished submitting the
92          * request)
93          */
94         try (HttpClientConnectionManager crtConnPool = getOrCreateConnectionPool(poolKey(asyncRequest.request()))) {
95             CrtAsyncRequestContext context = CrtAsyncRequestContext.builder()
96                                                                    .crtConnPool(crtConnPool)
97                                                                    .readBufferSize(this.readBufferSize)
98                                                                    .request(asyncRequest)
99                                                                    .build();
100 
101             return new CrtAsyncRequestExecutor().execute(context);
102         }
103     }
104 
105     /**
106      * Builder that allows configuration of the AWS CRT HTTP implementation.
107      */
108     public interface Builder extends SdkAsyncHttpClient.Builder<AwsCrtAsyncHttpClient.Builder> {
109 
110         /**
111          * The Maximum number of allowed concurrent requests. For HTTP/1.1 this is the same as max connections.
112          * @param maxConcurrency maximum concurrency per endpoint
113          * @return The builder of the method chaining.
114          */
maxConcurrency(Integer maxConcurrency)115         AwsCrtAsyncHttpClient.Builder maxConcurrency(Integer maxConcurrency);
116 
117         /**
118          * Configures the number of unread bytes that can be buffered in the
119          * client before we stop reading from the underlying TCP socket and wait for the Subscriber
120          * to read more data.
121          *
122          * @param readBufferSize The number of bytes that can be buffered.
123          * @return The builder of the method chaining.
124          */
readBufferSizeInBytes(Long readBufferSize)125         AwsCrtAsyncHttpClient.Builder readBufferSizeInBytes(Long readBufferSize);
126 
127         /**
128          * Sets the http proxy configuration to use for this client.
129          * @param proxyConfiguration The http proxy configuration to use
130          * @return The builder of the method chaining.
131          */
proxyConfiguration(ProxyConfiguration proxyConfiguration)132         AwsCrtAsyncHttpClient.Builder proxyConfiguration(ProxyConfiguration proxyConfiguration);
133 
134         /**
135          * Sets the http proxy configuration to use for this client.
136          *
137          * @param proxyConfigurationBuilderConsumer The consumer of the proxy configuration builder object.
138          * @return the builder for method chaining.
139          */
proxyConfiguration(Consumer<ProxyConfiguration.Builder> proxyConfigurationBuilderConsumer)140         AwsCrtAsyncHttpClient.Builder proxyConfiguration(Consumer<ProxyConfiguration.Builder> proxyConfigurationBuilderConsumer);
141 
142         /**
143          * Configure the health checks for all connections established by this client.
144          *
145          * <p>
146          * You can set a throughput threshold for a connection to be considered healthy.
147          * If a connection falls below this threshold ({@link ConnectionHealthConfiguration#minimumThroughputInBps()
148          * }) for the configurable amount
149          * of time ({@link ConnectionHealthConfiguration#minimumThroughputTimeout()}),
150          * then the connection is considered unhealthy and will be shut down.
151          *
152          * <p>
153          * By default, monitoring options are disabled. You can enable {@code healthChecks} by providing this configuration
154          * and specifying the options for monitoring for the connection manager.
155          * @param healthChecksConfiguration The health checks config to use
156          * @return The builder of the method chaining.
157          */
connectionHealthConfiguration(ConnectionHealthConfiguration healthChecksConfiguration)158         AwsCrtAsyncHttpClient.Builder connectionHealthConfiguration(ConnectionHealthConfiguration healthChecksConfiguration);
159 
160         /**
161          * A convenience method that creates an instance of the {@link ConnectionHealthConfiguration} builder, avoiding the
162          * need to create one manually via {@link ConnectionHealthConfiguration#builder()}.
163          *
164          * @param healthChecksConfigurationBuilder The health checks config builder to use
165          * @return The builder of the method chaining.
166          * @see #connectionHealthConfiguration(ConnectionHealthConfiguration)
167          */
connectionHealthConfiguration(Consumer<ConnectionHealthConfiguration.Builder> healthChecksConfigurationBuilder)168         AwsCrtAsyncHttpClient.Builder connectionHealthConfiguration(Consumer<ConnectionHealthConfiguration.Builder>
169                                                         healthChecksConfigurationBuilder);
170 
171         /**
172          * Configure the maximum amount of time that a connection should be allowed to remain open while idle.
173          * @param connectionMaxIdleTime the maximum amount of connection idle time
174          * @return The builder of the method chaining.
175          */
connectionMaxIdleTime(Duration connectionMaxIdleTime)176         AwsCrtAsyncHttpClient.Builder connectionMaxIdleTime(Duration connectionMaxIdleTime);
177 
178         /**
179          * The amount of time to wait when initially establishing a connection before giving up and timing out.
180          * @param connectionTimeout timeout
181          * @return The builder of the method chaining.
182          */
connectionTimeout(Duration connectionTimeout)183         AwsCrtAsyncHttpClient.Builder connectionTimeout(Duration connectionTimeout);
184 
185         /**
186          * Configure whether to enable {@code tcpKeepAlive} and relevant configuration for all connections established by this
187          * client.
188          *
189          * <p>
190          * By default, tcpKeepAlive is disabled. You can enable {@code tcpKeepAlive} by providing this configuration
191          * and specifying periodic TCP keepalive packet intervals and timeouts. This may be required for certain connections for
192          * longer durations than default socket timeouts.
193          *
194          * @param tcpKeepAliveConfiguration The TCP keep-alive configuration to use
195          * @return The builder of the method chaining.
196          */
tcpKeepAliveConfiguration(TcpKeepAliveConfiguration tcpKeepAliveConfiguration)197         AwsCrtAsyncHttpClient.Builder tcpKeepAliveConfiguration(TcpKeepAliveConfiguration tcpKeepAliveConfiguration);
198 
199         /**
200          * Configure whether to enable {@code tcpKeepAlive} and relevant configuration for all connections established by this
201          * client.
202          *
203          * <p>
204          * A convenience method that creates an instance of the {@link TcpKeepAliveConfiguration} builder, avoiding the
205          * need to create one manually via {@link TcpKeepAliveConfiguration#builder()}.
206          *
207          * @param tcpKeepAliveConfigurationBuilder The TCP keep-alive configuration builder to use
208          * @return The builder of the method chaining.
209          * @see #tcpKeepAliveConfiguration(TcpKeepAliveConfiguration)
210          */
tcpKeepAliveConfiguration(Consumer<TcpKeepAliveConfiguration.Builder> tcpKeepAliveConfigurationBuilder)211         AwsCrtAsyncHttpClient.Builder tcpKeepAliveConfiguration(Consumer<TcpKeepAliveConfiguration.Builder>
212                                               tcpKeepAliveConfigurationBuilder);
213 
214         /**
215          * Configure whether to enable a hybrid post-quantum key exchange option for the Transport Layer Security (TLS) network
216          * encryption protocol when communicating with services that support Post Quantum TLS. If Post Quantum cipher suites are
217          * not supported on the platform, the SDK will use the default TLS cipher suites.
218          *
219          * <p>
220          * See <a href="https://docs.aws.amazon.com/kms/latest/developerguide/pqtls.html">Using hybrid post-quantum TLS with AWS KMS</a>
221          *
222          * <p>
223          * It's disabled by default.
224          *
225          * @param postQuantumTlsEnabled whether to prefer Post Quantum TLS
226          * @return The builder of the method chaining.
227          */
postQuantumTlsEnabled(Boolean postQuantumTlsEnabled)228         AwsCrtAsyncHttpClient.Builder postQuantumTlsEnabled(Boolean postQuantumTlsEnabled);
229     }
230 
231     /**
232      * Factory that allows more advanced configuration of the AWS CRT HTTP implementation. Use {@link #builder()} to
233      * configure and construct an immutable instance of the factory.
234      */
235     private static final class DefaultAsyncBuilder
236         extends AwsCrtClientBuilderBase<AwsCrtAsyncHttpClient.Builder> implements Builder {
237 
238         @Override
build()239         public SdkAsyncHttpClient build() {
240             return new AwsCrtAsyncHttpClient(this, getAttributeMap().build()
241                                                                       .merge(SdkHttpConfigurationOption.GLOBAL_HTTP_DEFAULTS));
242         }
243 
244         @Override
buildWithDefaults(AttributeMap serviceDefaults)245         public SdkAsyncHttpClient buildWithDefaults(AttributeMap serviceDefaults) {
246             return new AwsCrtAsyncHttpClient(this, getAttributeMap().build()
247                                                                     .merge(serviceDefaults)
248                                                                     .merge(SdkHttpConfigurationOption.GLOBAL_HTTP_DEFAULTS));
249         }
250     }
251 }
252