@@ -76,15 +76,27 @@ public final class AwsCrtAsyncHttpClient implements SdkAsyncHttpClient {
76
76
private final int maxConnectionsPerEndpoint ;
77
77
private boolean isClosed = false ;
78
78
79
+ private static final Duration CRT_SDK_DEFAULT_CONNECTION_TIMEOUT = Duration .ofSeconds (3 );
80
+ // Override default connection timeout for Crt client to be in line with the CRT default:
81
+ // https://github.com/awslabs/aws-crt-java/blob/main/src/main/java/software/amazon/awssdk/crt/io/SocketOptions.java#L79
82
+ private static final AttributeMap CRT_HTTP_DEFAULTS =
83
+ AttributeMap .builder ()
84
+ .put (SdkHttpConfigurationOption .CONNECTION_TIMEOUT , CRT_SDK_DEFAULT_CONNECTION_TIMEOUT )
85
+ .build ();
86
+
79
87
private AwsCrtAsyncHttpClient (DefaultBuilder builder , AttributeMap config ) {
80
88
int maxConns = config .get (SdkHttpConfigurationOption .MAX_CONNECTIONS );
81
89
82
90
Validate .isPositive (maxConns , "maxConns" );
83
91
Validate .notNull (builder .cipherPreference , "cipherPreference" );
84
92
Validate .isPositive (builder .readBufferSize , "readBufferSize" );
85
93
94
+ if (Boolean .TRUE .equals (builder .standardOptions .get (SdkHttpConfigurationOption .TCP_KEEPALIVE ))) {
95
+ Validate .notNull (builder .tcpKeepAliveConfiguration , "tcpKeepAliveConfiguration must be provided when tcpKeepAlive is enabled" );
96
+ }
97
+
86
98
try (ClientBootstrap clientBootstrap = new ClientBootstrap (null , null );
87
- SocketOptions clientSocketOptions = new SocketOptions ( );
99
+ SocketOptions clientSocketOptions = buildSocketOptions ( builder , config );
88
100
TlsContextOptions clientTlsContextOptions = TlsContextOptions .createDefaultClient () // NOSONAR
89
101
.withCipherPreference (builder .cipherPreference )
90
102
.withVerifyPeer (!config .get (SdkHttpConfigurationOption .TRUST_ALL_CERTIFICATES ));
@@ -138,6 +150,34 @@ private HttpProxyOptions buildProxyOptions(ProxyConfiguration proxyConfiguration
138
150
return clientProxyOptions ;
139
151
}
140
152
153
+ private SocketOptions buildSocketOptions (DefaultBuilder builder , AttributeMap config ) {
154
+ SocketOptions clientSocketOptions = new SocketOptions ();
155
+
156
+ Duration connectionTimeout = config .get (SdkHttpConfigurationOption .CONNECTION_TIMEOUT );
157
+ if (connectionTimeout != null ) {
158
+ clientSocketOptions .connectTimeoutMs = (int ) Long .min (connectionTimeout .toMillis (), Integer .MAX_VALUE );
159
+ }
160
+
161
+ TcpKeepAliveConfiguration tcpKeepAliveConfiguration = builder .tcpKeepAliveConfiguration ;
162
+ if (tcpKeepAliveConfiguration != null ) {
163
+ long keepAliveIntervalSecs = tcpKeepAliveConfiguration .keepAliveInterval ().getSeconds ();
164
+ long keepAliveTimeoutSecs = tcpKeepAliveConfiguration .keepAliveTimeout ().getSeconds ();
165
+
166
+ clientSocketOptions .keepAliveIntervalSecs = (int ) Long .min (keepAliveIntervalSecs , Integer .MAX_VALUE );
167
+ clientSocketOptions .keepAliveTimeoutSecs = (int ) Long .min (keepAliveTimeoutSecs , Integer .MAX_VALUE );
168
+
169
+ }
170
+
171
+ SocketOptionsConfiguration socketOptionsConfiguration = builder .socketOptionsConfiguration ;
172
+ if (socketOptionsConfiguration != null ) {
173
+ clientSocketOptions .domain = socketOptionsConfiguration .domain ();
174
+ clientSocketOptions .type = socketOptionsConfiguration .type ();
175
+ }
176
+
177
+
178
+ return clientSocketOptions ;
179
+ }
180
+
141
181
/**
142
182
* Marks a Native CrtResource as owned by the current Java Object.
143
183
*
@@ -312,10 +352,10 @@ public interface Builder extends SdkAsyncHttpClient.Builder<AwsCrtAsyncHttpClien
312
352
Builder proxyConfiguration (Consumer <ProxyConfiguration .Builder > proxyConfigurationBuilderConsumer );
313
353
314
354
/**
315
- * Configure the health checks for for all connections established by this client.
355
+ * Configure the health checks for all connections established by this client.
316
356
*
317
357
* <p>
318
- * eg: you can set a throughput threshold for the a connection to be considered healthy.
358
+ * eg: you can set a throughput threshold for a connection to be considered healthy.
319
359
* If the connection falls below this threshold for a configurable amount of time,
320
360
* then the connection is considered unhealthy and will be shut down.
321
361
*
@@ -325,10 +365,10 @@ public interface Builder extends SdkAsyncHttpClient.Builder<AwsCrtAsyncHttpClien
325
365
Builder connectionHealthChecksConfiguration (ConnectionHealthChecksConfiguration healthChecksConfiguration );
326
366
327
367
/**
328
- * A convenience method to configure the health checks for for all connections established by this client.
368
+ * A convenience method to configure the health checks for all connections established by this client.
329
369
*
330
370
* <p>
331
- * eg: you can set a throughput threshold for the a connection to be considered healthy.
371
+ * eg: you can set a throughput threshold for a connection to be considered healthy.
332
372
* If the connection falls below this threshold for a configurable amount of time,
333
373
* then the connection is considered unhealthy and will be shut down.
334
374
*
@@ -340,9 +380,75 @@ Builder connectionHealthChecksConfiguration(Consumer<ConnectionHealthChecksConfi
340
380
healthChecksConfigurationBuilder );
341
381
342
382
/**
343
- * Configure the maximum amount of time that a connection should be allowed to remain open while idle.
383
+ * The amount of time to wait when initially establishing a connection before giving up and timing out. The maximum
384
+ * possible value, in ms, is the value of {@link Integer#MAX_VALUE}, any longer duration will be reduced to the maximum
385
+ * possible value. If not specified, the connection timeout duration will be set to value defined in
386
+ * {@link AwsCrtAsyncHttpClient#CRT_SDK_DEFAULT_CONNECTION_TIMEOUT}.
344
387
*/
345
388
Builder connectionMaxIdleTime (Duration connectionMaxIdleTime );
389
+
390
+ /**
391
+ * Configure connection socket timeout
392
+ */
393
+ Builder connectionTimeout (Duration connectionTimeout );
394
+
395
+ /**
396
+ * Configure whether to enable or disable TCP KeepAlive.
397
+ * The configuration will be passed to the socket option {@link java.net.SocketOptions#SO_KEEPALIVE}.
398
+ * <p>
399
+ * By default, this is disabled.
400
+ * <p>
401
+ * When enabled, the actual KeepAlive mechanism is dependent on the Operating System and therefore additional TCP
402
+ * KeepAlive values (like timeout, number of packets, etc) must be configured via the Operating System (sysctl on
403
+ * Linux/Mac, and Registry values on Windows).
404
+ *
405
+ * To enable, keepAlive interval and timeout must be additionally configured via {@link TcpKeepAliveConfiguration}
406
+ */
407
+ Builder tcpKeepAlive (Boolean keepConnectionAlive );
408
+
409
+ /**
410
+ * Configure TCP Keep-alive configuration for all connections established by this client.
411
+ *
412
+ * <p>
413
+ * If tcpKeepAlive is enabled, this is required configuration
414
+ * and specify periodic keepalive packet intervals and including timeouts
415
+ * This may be required for certain connections for longer durations than default socket timeouts
416
+ *
417
+ * @param tcpKeepAliveConfiguration The TCP keep-alive configuration to use
418
+ * @return The builder of the method chaining.
419
+ */
420
+ Builder tcpKeepAliveConfiguration (TcpKeepAliveConfiguration tcpKeepAliveConfiguration );
421
+
422
+ /**
423
+ * Configure TCP Keep-alive configuration for all connections established by this client.
424
+ *
425
+ * <p>
426
+ * If tcpKeepAlive is enabled, this is required configuration
427
+ * and specify periodic keepalive packet intervals and including timeouts
428
+ * This may be required for certain connections for longer durations than default socket timeouts
429
+ *
430
+ * @param tcpKeepAliveConfigurationBuilder The TCP keep-alive configuration builder to use
431
+ * @return The builder of the method chaining.
432
+ */
433
+ Builder tcpKeepAliveConfiguration (Consumer <TcpKeepAliveConfiguration .Builder >
434
+ tcpKeepAliveConfigurationBuilder );
435
+
436
+ /**
437
+ * Configure socket options configuration for alternative transports via socket domains and ty pes
438
+ *
439
+ * @param socketOptionsConfiguration The socket configuration to use
440
+ * @return The builder of the method chaining.
441
+ */
442
+ Builder socketOptionsConfiguration (SocketOptionsConfiguration socketOptionsConfiguration );
443
+
444
+ /**
445
+ * Configure socket options configuration for alternative transports via socket domains and ty pes
446
+ *
447
+ * @param socketOptionsConfigurationBuilder The socket configuration builder to use
448
+ * @return The builder of the method chaining.
449
+ */
450
+ Builder socketOptionsConfiguration (Consumer <SocketOptionsConfiguration .Builder >
451
+ socketOptionsConfigurationBuilder );
346
452
}
347
453
348
454
/**
@@ -355,20 +461,24 @@ private static final class DefaultBuilder implements Builder {
355
461
private int readBufferSize = DEFAULT_STREAM_WINDOW_SIZE ;
356
462
private ProxyConfiguration proxyConfiguration ;
357
463
private ConnectionHealthChecksConfiguration connectionHealthChecksConfiguration ;
464
+ private TcpKeepAliveConfiguration tcpKeepAliveConfiguration ;
465
+ private SocketOptionsConfiguration socketOptionsConfiguration ;
358
466
359
467
private DefaultBuilder () {
360
468
}
361
469
362
470
@ Override
363
471
public SdkAsyncHttpClient build () {
364
472
return new AwsCrtAsyncHttpClient (this , standardOptions .build ()
473
+ .merge (CRT_HTTP_DEFAULTS )
365
474
.merge (SdkHttpConfigurationOption .GLOBAL_HTTP_DEFAULTS ));
366
475
}
367
476
368
477
@ Override
369
478
public SdkAsyncHttpClient buildWithDefaults (AttributeMap serviceDefaults ) {
370
479
return new AwsCrtAsyncHttpClient (this , standardOptions .build ()
371
480
.merge (serviceDefaults )
481
+ .merge (CRT_HTTP_DEFAULTS )
372
482
.merge (SdkHttpConfigurationOption .GLOBAL_HTTP_DEFAULTS ));
373
483
}
374
484
@@ -417,15 +527,59 @@ public Builder connectionHealthChecksConfiguration(Consumer<ConnectionHealthChec
417
527
418
528
@ Override
419
529
public Builder connectionMaxIdleTime (Duration connectionMaxIdleTime ) {
530
+ Validate .isPositive (connectionMaxIdleTime , "connectionMaxIdleTime" );
420
531
standardOptions .put (SdkHttpConfigurationOption .CONNECTION_MAX_IDLE_TIMEOUT , connectionMaxIdleTime );
421
532
return this ;
422
533
}
423
534
535
+ @ Override
536
+ public Builder connectionTimeout (Duration connectionTimeout ) {
537
+ Validate .isPositive (connectionTimeout , "connectionTimeout" );
538
+ standardOptions .put (SdkHttpConfigurationOption .CONNECTION_TIMEOUT , connectionTimeout );
539
+ return this ;
540
+ }
541
+
542
+ @ Override
543
+ public Builder tcpKeepAlive (Boolean keepConnectionAlive ) {
544
+ Validate .notNull (keepConnectionAlive , "keepConnectionAlive" );
545
+ standardOptions .put (SdkHttpConfigurationOption .TCP_KEEPALIVE , keepConnectionAlive );
546
+ return this ;
547
+ }
548
+
549
+ @ Override
550
+ public Builder tcpKeepAliveConfiguration (TcpKeepAliveConfiguration tcpKeepAliveConfiguration ) {
551
+ this .tcpKeepAliveConfiguration = tcpKeepAliveConfiguration ;
552
+ return this ;
553
+ }
554
+
555
+ @ Override
556
+ public Builder tcpKeepAliveConfiguration (Consumer <TcpKeepAliveConfiguration .Builder >
557
+ tcpKeepAliveConfigurationBuilder ) {
558
+ TcpKeepAliveConfiguration .Builder builder = TcpKeepAliveConfiguration .builder ();
559
+ tcpKeepAliveConfigurationBuilder .accept (builder );
560
+ return tcpKeepAliveConfiguration (builder .build ());
561
+ }
562
+
424
563
@ Override
425
564
public Builder proxyConfiguration (Consumer <ProxyConfiguration .Builder > proxyConfigurationBuilderConsumer ) {
426
565
ProxyConfiguration .Builder builder = ProxyConfiguration .builder ();
427
566
proxyConfigurationBuilderConsumer .accept (builder );
428
567
return proxyConfiguration (builder .build ());
429
568
}
569
+
570
+
571
+ @ Override
572
+ public Builder socketOptionsConfiguration (SocketOptionsConfiguration socketOptionsConfiguration ) {
573
+ this .socketOptionsConfiguration = socketOptionsConfiguration ;
574
+ return this ;
575
+ }
576
+
577
+ @ Override
578
+ public Builder socketOptionsConfiguration (Consumer <SocketOptionsConfiguration .Builder >
579
+ socketOptionsConfigurationBuilder ) {
580
+ SocketOptionsConfiguration .Builder builder = SocketOptionsConfiguration .builder ();
581
+ socketOptionsConfigurationBuilder .accept (builder );
582
+ return socketOptionsConfiguration (builder .build ());
583
+ }
430
584
}
431
585
}
0 commit comments