46
46
import software .amazon .awssdk .utils .AttributeMap ;
47
47
import software .amazon .awssdk .utils .IoUtils ;
48
48
import software .amazon .awssdk .utils .Logger ;
49
+ import software .amazon .awssdk .utils .NumericUtils ;
49
50
import software .amazon .awssdk .utils .Validate ;
50
51
51
52
/**
@@ -76,15 +77,27 @@ public final class AwsCrtAsyncHttpClient implements SdkAsyncHttpClient {
76
77
private final int maxConnectionsPerEndpoint ;
77
78
private boolean isClosed = false ;
78
79
80
+ private static final Duration CRT_SDK_DEFAULT_CONNECTION_TIMEOUT = Duration .ofSeconds (3 );
81
+ // Override default connection timeout for Crt client to be in line with the CRT default:
82
+ // https://github.com/awslabs/aws-crt-java/blob/main/src/main/java/software/amazon/awssdk/crt/io/SocketOptions.java#L79
83
+ private static final AttributeMap CRT_HTTP_DEFAULTS =
84
+ AttributeMap .builder ()
85
+ .put (SdkHttpConfigurationOption .CONNECTION_TIMEOUT , CRT_SDK_DEFAULT_CONNECTION_TIMEOUT )
86
+ .build ();
87
+
79
88
private AwsCrtAsyncHttpClient (DefaultBuilder builder , AttributeMap config ) {
80
89
int maxConns = config .get (SdkHttpConfigurationOption .MAX_CONNECTIONS );
81
90
82
91
Validate .isPositive (maxConns , "maxConns" );
83
92
Validate .notNull (builder .cipherPreference , "cipherPreference" );
84
93
Validate .isPositive (builder .readBufferSize , "readBufferSize" );
85
94
95
+ if (Boolean .TRUE .equals (builder .standardOptions .get (SdkHttpConfigurationOption .TCP_KEEPALIVE ))) {
96
+ Validate .notNull (builder .tcpKeepAliveConfiguration , "tcpKeepAliveConfiguration must be provided when tcpKeepAlive is enabled" );
97
+ }
98
+
86
99
try (ClientBootstrap clientBootstrap = new ClientBootstrap (null , null );
87
- SocketOptions clientSocketOptions = new SocketOptions ( );
100
+ SocketOptions clientSocketOptions = buildSocketOptions ( builder , config );
88
101
TlsContextOptions clientTlsContextOptions = TlsContextOptions .createDefaultClient () // NOSONAR
89
102
.withCipherPreference (builder .cipherPreference )
90
103
.withVerifyPeer (!config .get (SdkHttpConfigurationOption .TRUST_ALL_CERTIFICATES ));
@@ -138,6 +151,24 @@ private HttpProxyOptions buildProxyOptions(ProxyConfiguration proxyConfiguration
138
151
return clientProxyOptions ;
139
152
}
140
153
154
+ private SocketOptions buildSocketOptions (DefaultBuilder builder , AttributeMap config ) {
155
+ SocketOptions clientSocketOptions = new SocketOptions ();
156
+
157
+ Duration connectionTimeout = config .get (SdkHttpConfigurationOption .CONNECTION_TIMEOUT );
158
+ if (connectionTimeout != null ) {
159
+ clientSocketOptions .connectTimeoutMs = NumericUtils .saturatedCast (connectionTimeout .toMillis ());
160
+ }
161
+
162
+ TcpKeepAliveConfiguration tcpKeepAliveConfiguration = builder .tcpKeepAliveConfiguration ;
163
+ if (tcpKeepAliveConfiguration != null ) {
164
+ clientSocketOptions .keepAliveIntervalSecs = NumericUtils .saturatedCast (tcpKeepAliveConfiguration .keepAliveInterval ().getSeconds ());
165
+ clientSocketOptions .keepAliveTimeoutSecs = NumericUtils .saturatedCast (tcpKeepAliveConfiguration .keepAliveTimeout ().getSeconds ());
166
+
167
+ }
168
+
169
+ return clientSocketOptions ;
170
+ }
171
+
141
172
/**
142
173
* Marks a Native CrtResource as owned by the current Java Object.
143
174
*
@@ -312,10 +343,10 @@ public interface Builder extends SdkAsyncHttpClient.Builder<AwsCrtAsyncHttpClien
312
343
Builder proxyConfiguration (Consumer <ProxyConfiguration .Builder > proxyConfigurationBuilderConsumer );
313
344
314
345
/**
315
- * Configure the health checks for for all connections established by this client.
346
+ * Configure the health checks for all connections established by this client.
316
347
*
317
348
* <p>
318
- * eg: you can set a throughput threshold for the a connection to be considered healthy.
349
+ * eg: you can set a throughput threshold for a connection to be considered healthy.
319
350
* If the connection falls below this threshold for a configurable amount of time,
320
351
* then the connection is considered unhealthy and will be shut down.
321
352
*
@@ -325,10 +356,10 @@ public interface Builder extends SdkAsyncHttpClient.Builder<AwsCrtAsyncHttpClien
325
356
Builder connectionHealthChecksConfiguration (ConnectionHealthChecksConfiguration healthChecksConfiguration );
326
357
327
358
/**
328
- * A convenience method to configure the health checks for for all connections established by this client.
359
+ * A convenience method to configure the health checks for all connections established by this client.
329
360
*
330
361
* <p>
331
- * eg: you can set a throughput threshold for the a connection to be considered healthy.
362
+ * eg: you can set a throughput threshold for a connection to be considered healthy.
332
363
* If the connection falls below this threshold for a configurable amount of time,
333
364
* then the connection is considered unhealthy and will be shut down.
334
365
*
@@ -340,9 +371,58 @@ Builder connectionHealthChecksConfiguration(Consumer<ConnectionHealthChecksConfi
340
371
healthChecksConfigurationBuilder );
341
372
342
373
/**
343
- * Configure the maximum amount of time that a connection should be allowed to remain open while idle.
374
+ * The amount of time to wait when initially establishing a connection before giving up and timing out. The maximum
375
+ * possible value, in ms, is the value of {@link Integer#MAX_VALUE}, any longer duration will be reduced to the maximum
376
+ * possible value. If not specified, the connection timeout duration will be set to value defined in
377
+ * {@link AwsCrtAsyncHttpClient#CRT_SDK_DEFAULT_CONNECTION_TIMEOUT}.
344
378
*/
345
379
Builder connectionMaxIdleTime (Duration connectionMaxIdleTime );
380
+
381
+ /**
382
+ * Configure connection socket timeout
383
+ */
384
+ Builder connectionTimeout (Duration connectionTimeout );
385
+
386
+ /**
387
+ * Configure whether to enable or disable TCP KeepAlive.
388
+ * The configuration will be passed to the socket option {@link java.net.SocketOptions#SO_KEEPALIVE}.
389
+ * <p>
390
+ * By default, this is disabled.
391
+ * <p>
392
+ * When enabled, the actual KeepAlive mechanism is dependent on the Operating System and therefore additional TCP
393
+ * KeepAlive values (like timeout, number of packets, etc) must be configured via the Operating System (sysctl on
394
+ * Linux/Mac, and Registry values on Windows).
395
+ *
396
+ * To enable, keepAlive interval and timeout must be additionally configured via {@link TcpKeepAliveConfiguration}
397
+ */
398
+ Builder tcpKeepAlive (Boolean keepConnectionAlive );
399
+
400
+ /**
401
+ * Configure TCP Keep-alive configuration for all connections established by this client.
402
+ *
403
+ * <p>
404
+ * If tcpKeepAlive is enabled, this is required configuration
405
+ * and specify periodic keepalive packet intervals and including timeouts
406
+ * This may be required for certain connections for longer durations than default socket timeouts
407
+ *
408
+ * @param tcpKeepAliveConfiguration The TCP keep-alive configuration to use
409
+ * @return The builder of the method chaining.
410
+ */
411
+ Builder tcpKeepAliveConfiguration (TcpKeepAliveConfiguration tcpKeepAliveConfiguration );
412
+
413
+ /**
414
+ * Configure TCP Keep-alive configuration for all connections established by this client.
415
+ *
416
+ * <p>
417
+ * If tcpKeepAlive is enabled, this is required configuration
418
+ * and specify periodic keepalive packet intervals and including timeouts
419
+ * This may be required for certain connections for longer durations than default socket timeouts
420
+ *
421
+ * @param tcpKeepAliveConfigurationBuilder The TCP keep-alive configuration builder to use
422
+ * @return The builder of the method chaining.
423
+ */
424
+ Builder tcpKeepAliveConfiguration (Consumer <TcpKeepAliveConfiguration .Builder >
425
+ tcpKeepAliveConfigurationBuilder );
346
426
}
347
427
348
428
/**
@@ -355,20 +435,23 @@ private static final class DefaultBuilder implements Builder {
355
435
private int readBufferSize = DEFAULT_STREAM_WINDOW_SIZE ;
356
436
private ProxyConfiguration proxyConfiguration ;
357
437
private ConnectionHealthChecksConfiguration connectionHealthChecksConfiguration ;
438
+ private TcpKeepAliveConfiguration tcpKeepAliveConfiguration ;
358
439
359
440
private DefaultBuilder () {
360
441
}
361
442
362
443
@ Override
363
444
public SdkAsyncHttpClient build () {
364
445
return new AwsCrtAsyncHttpClient (this , standardOptions .build ()
446
+ .merge (CRT_HTTP_DEFAULTS )
365
447
.merge (SdkHttpConfigurationOption .GLOBAL_HTTP_DEFAULTS ));
366
448
}
367
449
368
450
@ Override
369
451
public SdkAsyncHttpClient buildWithDefaults (AttributeMap serviceDefaults ) {
370
452
return new AwsCrtAsyncHttpClient (this , standardOptions .build ()
371
453
.merge (serviceDefaults )
454
+ .merge (CRT_HTTP_DEFAULTS )
372
455
.merge (SdkHttpConfigurationOption .GLOBAL_HTTP_DEFAULTS ));
373
456
}
374
457
@@ -417,10 +500,39 @@ public Builder connectionHealthChecksConfiguration(Consumer<ConnectionHealthChec
417
500
418
501
@ Override
419
502
public Builder connectionMaxIdleTime (Duration connectionMaxIdleTime ) {
503
+ Validate .isPositive (connectionMaxIdleTime , "connectionMaxIdleTime" );
420
504
standardOptions .put (SdkHttpConfigurationOption .CONNECTION_MAX_IDLE_TIMEOUT , connectionMaxIdleTime );
421
505
return this ;
422
506
}
423
507
508
+ @ Override
509
+ public Builder connectionTimeout (Duration connectionTimeout ) {
510
+ Validate .isPositive (connectionTimeout , "connectionTimeout" );
511
+ standardOptions .put (SdkHttpConfigurationOption .CONNECTION_TIMEOUT , connectionTimeout );
512
+ return this ;
513
+ }
514
+
515
+ @ Override
516
+ public Builder tcpKeepAlive (Boolean keepConnectionAlive ) {
517
+ Validate .notNull (keepConnectionAlive , "keepConnectionAlive" );
518
+ standardOptions .put (SdkHttpConfigurationOption .TCP_KEEPALIVE , keepConnectionAlive );
519
+ return this ;
520
+ }
521
+
522
+ @ Override
523
+ public Builder tcpKeepAliveConfiguration (TcpKeepAliveConfiguration tcpKeepAliveConfiguration ) {
524
+ this .tcpKeepAliveConfiguration = tcpKeepAliveConfiguration ;
525
+ return this ;
526
+ }
527
+
528
+ @ Override
529
+ public Builder tcpKeepAliveConfiguration (Consumer <TcpKeepAliveConfiguration .Builder >
530
+ tcpKeepAliveConfigurationBuilder ) {
531
+ TcpKeepAliveConfiguration .Builder builder = TcpKeepAliveConfiguration .builder ();
532
+ tcpKeepAliveConfigurationBuilder .accept (builder );
533
+ return tcpKeepAliveConfiguration (builder .build ());
534
+ }
535
+
424
536
@ Override
425
537
public Builder proxyConfiguration (Consumer <ProxyConfiguration .Builder > proxyConfigurationBuilderConsumer ) {
426
538
ProxyConfiguration .Builder builder = ProxyConfiguration .builder ();
0 commit comments