Skip to content

Commit 5e2cbf3

Browse files
committed
Add the ability to configure SocketOptions on the AwsCrtAsyncHttpClient. This is necessary for any clients with long-running connections that exceed default socket timeouts of services along the call path, and need to enable keep-alive settings which the CRT client supports, but the Java client wasn't exposing to callers
1 parent d6b02d5 commit 5e2cbf3

File tree

5 files changed

+389
-7
lines changed

5 files changed

+389
-7
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "feature",
3+
"category": "AWS SDK for Java v2",
4+
"contributor": "nikp",
5+
"description": "Add the ability to configure SocketOptions on the AwsCrtAsyncHttpClient to allow explicit enabling of keep-alive signals for long-running connections"
6+
}

http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/AwsCrtAsyncHttpClient.java

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ private AwsCrtAsyncHttpClient(DefaultBuilder builder, AttributeMap config) {
8484
Validate.isPositive(builder.readBufferSize, "readBufferSize");
8585

8686
try (ClientBootstrap clientBootstrap = new ClientBootstrap(null, null);
87-
SocketOptions clientSocketOptions = new SocketOptions();
87+
SocketOptions clientSocketOptions = buildSocketOptions(builder.socketOptionsConfiguration);
8888
TlsContextOptions clientTlsContextOptions = TlsContextOptions.createDefaultClient() // NOSONAR
8989
.withCipherPreference(builder.cipherPreference)
9090
.withVerifyPeer(!config.get(SdkHttpConfigurationOption.TRUST_ALL_CERTIFICATES));
@@ -138,6 +138,22 @@ private HttpProxyOptions buildProxyOptions(ProxyConfiguration proxyConfiguration
138138
return clientProxyOptions;
139139
}
140140

141+
private SocketOptions buildSocketOptions(SocketOptionsConfiguration socketOptionsConfiguration) {
142+
SocketOptions clientSocketOptions = new SocketOptions();
143+
144+
if (socketOptionsConfiguration == null) {
145+
return clientSocketOptions;
146+
}
147+
148+
clientSocketOptions.domain = socketOptionsConfiguration.domain();
149+
clientSocketOptions.type = socketOptionsConfiguration.type();
150+
clientSocketOptions.connectTimeoutMs = socketOptionsConfiguration.connectTimeoutMs();
151+
clientSocketOptions.keepAliveIntervalSecs = socketOptionsConfiguration.keepAliveIntervalSecs();
152+
clientSocketOptions.keepAliveTimeoutSecs = socketOptionsConfiguration.keepAliveTimeoutSecs();
153+
154+
return clientSocketOptions;
155+
}
156+
141157
/**
142158
* Marks a Native CrtResource as owned by the current Java Object.
143159
*
@@ -312,10 +328,10 @@ public interface Builder extends SdkAsyncHttpClient.Builder<AwsCrtAsyncHttpClien
312328
Builder proxyConfiguration(Consumer<ProxyConfiguration.Builder> proxyConfigurationBuilderConsumer);
313329

314330
/**
315-
* Configure the health checks for for all connections established by this client.
331+
* Configure the health checks for all connections established by this client.
316332
*
317333
* <p>
318-
* eg: you can set a throughput threshold for the a connection to be considered healthy.
334+
* eg: you can set a throughput threshold for a connection to be considered healthy.
319335
* If the connection falls below this threshold for a configurable amount of time,
320336
* then the connection is considered unhealthy and will be shut down.
321337
*
@@ -325,10 +341,10 @@ public interface Builder extends SdkAsyncHttpClient.Builder<AwsCrtAsyncHttpClien
325341
Builder connectionHealthChecksConfiguration(ConnectionHealthChecksConfiguration healthChecksConfiguration);
326342

327343
/**
328-
* A convenience method to configure the health checks for for all connections established by this client.
344+
* A convenience method to configure the health checks for all connections established by this client.
329345
*
330346
* <p>
331-
* eg: you can set a throughput threshold for the a connection to be considered healthy.
347+
* eg: you can set a throughput threshold for a connection to be considered healthy.
332348
* If the connection falls below this threshold for a configurable amount of time,
333349
* then the connection is considered unhealthy and will be shut down.
334350
*
@@ -343,6 +359,34 @@ Builder connectionHealthChecksConfiguration(Consumer<ConnectionHealthChecksConfi
343359
* Configure the maximum amount of time that a connection should be allowed to remain open while idle.
344360
*/
345361
Builder connectionMaxIdleTime(Duration connectionMaxIdleTime);
362+
363+
/**
364+
* Configure socket options configuration for all connections established by this client.
365+
*
366+
* <p>
367+
* eg: you can override socket domains and types, connection timeouts,
368+
* and enable periodic keepalive packets which are disabled out of the box, including keepalive timeouts
369+
* This may be required for certain connections for longer durations than default socket timeouts
370+
*
371+
* @param socketOptionsConfiguration The socket configuration to use
372+
* @return The builder of the method chaining.
373+
*/
374+
Builder socketOptionsConfiguration(SocketOptionsConfiguration socketOptionsConfiguration);
375+
376+
/**
377+
* A convenience method to configure socket options configuration for all connections established by this client.
378+
*
379+
* <p>
380+
* eg: you can override socket domains and types, connection timeouts,
381+
* and enable periodic keepalive packets which are disabled out of the box, including keepalive timeouts
382+
* This may be required for certain connections for longer durations than default socket timeouts
383+
*
384+
* @param socketOptionsConfigurationBuilder The socket configuration builder to use
385+
* @return The builder of the method chaining.
386+
* @see #socketOptionsConfiguration(SocketOptionsConfiguration)
387+
*/
388+
Builder socketOptionsConfiguration(Consumer<SocketOptionsConfiguration.Builder>
389+
socketOptionsConfigurationBuilder);
346390
}
347391

348392
/**
@@ -355,6 +399,7 @@ private static final class DefaultBuilder implements Builder {
355399
private int readBufferSize = DEFAULT_STREAM_WINDOW_SIZE;
356400
private ProxyConfiguration proxyConfiguration;
357401
private ConnectionHealthChecksConfiguration connectionHealthChecksConfiguration;
402+
private SocketOptionsConfiguration socketOptionsConfiguration;
358403

359404
private DefaultBuilder() {
360405
}
@@ -427,5 +472,20 @@ public Builder proxyConfiguration(Consumer<ProxyConfiguration.Builder> proxyConf
427472
proxyConfigurationBuilderConsumer.accept(builder);
428473
return proxyConfiguration(builder.build());
429474
}
475+
476+
477+
@Override
478+
public Builder socketOptionsConfiguration(SocketOptionsConfiguration socketOptionsConfiguration) {
479+
this.socketOptionsConfiguration = socketOptionsConfiguration;
480+
return this;
481+
}
482+
483+
@Override
484+
public Builder socketOptionsConfiguration(Consumer<SocketOptionsConfiguration.Builder>
485+
socketOptionsConfigurationBuilder) {
486+
SocketOptionsConfiguration.Builder builder = SocketOptionsConfiguration.builder();
487+
socketOptionsConfigurationBuilder.accept(builder);
488+
return socketOptionsConfiguration(builder.build());
489+
}
430490
}
431491
}

http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/ConnectionHealthChecksConfiguration.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
import software.amazon.awssdk.utils.Validate;
2222

2323
/**
24-
* Configuration that defines health checks for for all connections established by
25-
* the{@link ConnectionHealthChecksConfiguration}.
24+
* Configuration that defines health checks for all connections established by
25+
* the {@link ConnectionHealthChecksConfiguration}.
2626
*
2727
* <b>NOTE:</b> This is a Preview API and is subject to change so it should not be used in production.
2828
*/
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
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 software.amazon.awssdk.annotations.SdkPreviewApi;
19+
import software.amazon.awssdk.annotations.SdkPublicApi;
20+
import software.amazon.awssdk.crt.io.SocketOptions;
21+
import software.amazon.awssdk.utils.Validate;
22+
23+
/**
24+
* Configuration that defines socket options for all connections established by
25+
* the {@link SocketOptionsConfiguration}.
26+
*
27+
* <b>NOTE:</b> This is a Preview API and is subject to change so it should not be used in production.
28+
*/
29+
@SdkPublicApi
30+
@SdkPreviewApi
31+
public final class SocketOptionsConfiguration {
32+
33+
private final SocketOptions.SocketDomain domain;
34+
private final SocketOptions.SocketType type;
35+
private final int connectTimeoutMs;
36+
private final int keepAliveIntervalSecs;
37+
private final int keepAliveTimeoutSecs;
38+
39+
private SocketOptionsConfiguration(DefaultSocketOptionsConfigurationBuilder builder) {
40+
this.domain = Validate.paramNotNull(builder.domain,
41+
"domain");
42+
this.type = Validate.paramNotNull(builder.type,
43+
"type");
44+
this.connectTimeoutMs = Validate.isPositive(builder.connectTimeoutMs,
45+
"connectTimeoutMs");
46+
this.keepAliveIntervalSecs = Validate.isNotNegative(builder.keepAliveIntervalSecs,
47+
"keepAliveIntervalSecs");
48+
this.keepAliveTimeoutSecs = Validate.isNotNegative(builder.keepAliveTimeoutSecs,
49+
"keepAliveTimeoutSecs");
50+
}
51+
52+
/**
53+
* @return socket domain
54+
*/
55+
public SocketOptions.SocketDomain domain() {
56+
return domain;
57+
}
58+
59+
/**
60+
* @return socket type
61+
*/
62+
public SocketOptions.SocketType type() {
63+
return type;
64+
}
65+
66+
/**
67+
* @return number of milliseconds before a connection will be considered timed out
68+
*/
69+
public int connectTimeoutMs() {
70+
return connectTimeoutMs;
71+
}
72+
73+
/**
74+
* @return number of seconds between TCP keepalive packets being sent to the peer
75+
*/
76+
public int keepAliveIntervalSecs() {
77+
return keepAliveIntervalSecs;
78+
}
79+
80+
/**
81+
* @return number of seconds to wait for a keepalive response before considering the connection timed out
82+
*/
83+
public int keepAliveTimeoutSecs() {
84+
return keepAliveTimeoutSecs;
85+
}
86+
87+
public static Builder builder() {
88+
return new DefaultSocketOptionsConfigurationBuilder();
89+
}
90+
91+
/**
92+
* A builder for {@link SocketOptionsConfiguration}.
93+
*
94+
* <p>All implementations of this interface are mutable and not thread safe.</p>
95+
*/
96+
public interface Builder {
97+
98+
/**
99+
* Sets the socket domain
100+
* @param domain socket domain
101+
* @return Builder
102+
*/
103+
Builder domain(SocketOptions.SocketDomain domain);
104+
105+
/**
106+
* Sets the socket type
107+
* @param type socket type
108+
* @return Builder
109+
*/
110+
Builder type(SocketOptions.SocketType type);
111+
112+
/**
113+
* Sets the number of milliseconds before a connection will be considered timed out
114+
* @param connectTimeoutMs number of milliseconds before a connection will be considered timed out
115+
* @return Builder
116+
*/
117+
Builder connectTimeoutMs(int connectTimeoutMs);
118+
119+
/**
120+
* Sets the number of seconds between TCP keepalive packets being sent to the peer
121+
* @param keepAliveIntervalSecs number of seconds between TCP keepalive packets being sent to the peer
122+
* @return Builder
123+
*/
124+
Builder keepAliveIntervalSecs(int keepAliveIntervalSecs);
125+
126+
/**
127+
* Sets the number of seconds to wait for a keepalive response before considering the connection timed out
128+
* @param keepAliveTimeoutSecs number of seconds to wait for a keepalive response before considering the connection timed out
129+
* @return Builder
130+
*/
131+
Builder keepAliveTimeoutSecs(int keepAliveTimeoutSecs);
132+
133+
SocketOptionsConfiguration build();
134+
}
135+
136+
/**
137+
* An SDK-internal implementation of {@link Builder}.
138+
*/
139+
private static final class DefaultSocketOptionsConfigurationBuilder implements Builder {
140+
141+
private SocketOptions.SocketDomain domain = SocketOptions.SocketDomain.IPv6;
142+
private SocketOptions.SocketType type = SocketOptions.SocketType.STREAM;
143+
private int connectTimeoutMs = 3000;
144+
private int keepAliveIntervalSecs = 0;
145+
private int keepAliveTimeoutSecs = 0;
146+
147+
private DefaultSocketOptionsConfigurationBuilder() {
148+
}
149+
150+
/**
151+
* Sets the socket domain
152+
* Default: {@link SocketOptions.SocketDomain#IPv6}
153+
* @param domain socket domain
154+
* @return Builder
155+
*/
156+
@Override
157+
public Builder domain(SocketOptions.SocketDomain domain) {
158+
this.domain = domain;
159+
return this;
160+
}
161+
162+
/**
163+
* Sets the socket type
164+
* Default: {@link SocketOptions.SocketType#STREAM}
165+
* @param type socket type
166+
* @return Builder
167+
*/
168+
@Override
169+
public Builder type(SocketOptions.SocketType type) {
170+
this.type = type;
171+
return this;
172+
}
173+
174+
/**
175+
* Sets the number of milliseconds before a connection will be considered timed out
176+
* Default: 3000ms
177+
* @param connectTimeoutMs number of milliseconds before a connection will be considered timed out
178+
* @return Builder
179+
*/
180+
@Override
181+
public Builder connectTimeoutMs(int connectTimeoutMs) {
182+
this.connectTimeoutMs = connectTimeoutMs;
183+
return this;
184+
}
185+
186+
/**
187+
* Sets the number of seconds between TCP keepalive packets being sent to the peer
188+
* Default: 0 disables keepalive
189+
* @param keepAliveIntervalSecs number of seconds between TCP keepalive packets being sent to the peer
190+
* @return Builder
191+
*/
192+
@Override
193+
public Builder keepAliveIntervalSecs(int keepAliveIntervalSecs) {
194+
this.keepAliveIntervalSecs = keepAliveIntervalSecs;
195+
return this;
196+
}
197+
198+
/**
199+
* Sets the number of seconds to wait for a keepalive response before considering the connection timed out
200+
* Default: 0 disables keepalive
201+
* @param keepAliveTimeoutSecs number of seconds to wait for a keepalive response before considering the connection timed out
202+
* @return Builder
203+
*/
204+
@Override
205+
public Builder keepAliveTimeoutSecs(int keepAliveTimeoutSecs) {
206+
this.keepAliveTimeoutSecs = keepAliveTimeoutSecs;
207+
return this;
208+
}
209+
210+
@Override
211+
public SocketOptionsConfiguration build() {
212+
return new SocketOptionsConfiguration(this);
213+
}
214+
}
215+
}

0 commit comments

Comments
 (0)