Skip to content

Commit 29a6ede

Browse files
committed
feat(sdk-core): Add User-Agent header with ft/warmup marker to HTTP client warm-up calls
1 parent 56af08a commit 29a6ede

5 files changed

Lines changed: 58 additions & 4 deletions

File tree

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/loader/AsyncHttpClientWarmer.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,10 @@ private static Iterable<SdkAsyncHttpService> discoverServices() {
8686
@Override
8787
public void warmAll() {
8888
URI endpoint = endpointProvider.get();
89+
String userAgent = HttpClientWarmer.warmUpUserAgent();
8990
WarmUpDiscovery.forEachDiscovered(services.iterator(), service -> {
9091
SdkAsyncHttpClient client = service.createAsyncHttpClientFactory().buildWithDefaults(AttributeMap.empty());
91-
warmClient(client, endpoint);
92+
warmClient(client, endpoint, userAgent);
9293
});
9394
}
9495

@@ -97,11 +98,12 @@ public void warmAll() {
9798
* failure or timeout is logged and swallowed. We block on the execute future (bounded) because the bundled async
9899
* clients complete it only after the body is drained, so its completion implies the full path was exercised.
99100
*/
100-
private void warmClient(SdkAsyncHttpClient client, URI endpoint) {
101+
private void warmClient(SdkAsyncHttpClient client, URI endpoint, String userAgent) {
101102
try {
102103
SdkHttpFullRequest httpRequest = SdkHttpFullRequest.builder()
103104
.method(SdkHttpMethod.GET)
104105
.uri(endpoint)
106+
.putHeader(HEADER_USER_AGENT, userAgent)
105107
.build();
106108
AsyncExecuteRequest request = AsyncExecuteRequest.builder()
107109
.request(httpRequest)

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/loader/HttpClientWarmer.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,42 @@
1515

1616
package software.amazon.awssdk.core.internal.http.loader;
1717

18+
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.FEATURE_METADATA;
19+
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.appendSpaceAndField;
20+
1821
import software.amazon.awssdk.annotations.SdkInternalApi;
22+
import software.amazon.awssdk.core.internal.useragent.SdkUserAgentBuilder;
23+
import software.amazon.awssdk.core.util.SystemUserAgent;
1924

2025
/**
2126
* Warms the sync or async HTTP clients on the classpath for CRaC priming.
2227
*/
2328
@SdkInternalApi
2429
public interface HttpClientWarmer {
2530

31+
/**
32+
* The {@code User-Agent} header name added to warm-up requests.
33+
*/
34+
String HEADER_USER_AGENT = "User-Agent";
35+
36+
/**
37+
* The user agent feature marker for SdkWarmUp.
38+
*/
39+
String WARM_UP_FEATURE_ID = "warmup";
40+
2641
/**
2742
* Warms every HTTP client found on the classpath. Best-effort; never throws.
2843
*/
2944
void warmAll();
45+
46+
/**
47+
* Builds the {@code User-Agent} header value for warm-up requests: the system user agent (SDK version, Java version, OS,
48+
* etc.) plus the {@link #WARM_UP_FEATURE_ID} feature marker.
49+
*/
50+
static String warmUpUserAgent() {
51+
StringBuilder uaString =
52+
new StringBuilder(SdkUserAgentBuilder.buildSystemUserAgentString(SystemUserAgent.getOrCreate()));
53+
appendSpaceAndField(uaString, FEATURE_METADATA, WARM_UP_FEATURE_ID);
54+
return uaString.toString();
55+
}
3056
}

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/loader/SyncHttpClientWarmer.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,21 +79,23 @@ private static Iterable<SdkHttpService> discoverServices() {
7979
@Override
8080
public void warmAll() {
8181
URI endpoint = endpointProvider.get();
82+
String userAgent = HttpClientWarmer.warmUpUserAgent();
8283
WarmUpDiscovery.forEachDiscovered(services.iterator(), service -> {
8384
SdkHttpClient client = service.createHttpClientBuilder().buildWithDefaults(AttributeMap.empty());
84-
warmClient(client, endpoint);
85+
warmClient(client, endpoint, userAgent);
8586
});
8687
}
8788

8889
/**
8990
* Sends the warm-up {@code GET} to {@code endpoint}, drains the response body, and closes the client. Best-effort: the
9091
* goal is JIT compilation, not a successful request, so any failure is logged and swallowed.
9192
*/
92-
private void warmClient(SdkHttpClient client, URI endpoint) {
93+
private void warmClient(SdkHttpClient client, URI endpoint, String userAgent) {
9394
try {
9495
SdkHttpRequest httpRequest = SdkHttpRequest.builder()
9596
.method(SdkHttpMethod.GET)
9697
.uri(endpoint)
98+
.putHeader(HEADER_USER_AGENT, userAgent)
9799
.build();
98100
HttpExecuteRequest request = HttpExecuteRequest.builder()
99101
.request(httpRequest)

core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/loader/AsyncHttpClientWarmerTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,18 @@ void warmAll_whenInvoked_issuesGetToResolvedEndpoint() {
7777
assertThat(request.getValue().request().getUri()).isEqualTo(ENDPOINT);
7878
}
7979

80+
@Test
81+
void warmAll_whenInvoked_addsWarmUpUserAgentHeader() {
82+
SdkAsyncHttpClient client = stubClient(emptyBody());
83+
ArgumentCaptor<AsyncExecuteRequest> request = ArgumentCaptor.forClass(AsyncExecuteRequest.class);
84+
85+
warmer(serviceFor(client)).warmAll();
86+
87+
verify(client).execute(request.capture());
88+
assertThat(request.getValue().request().firstMatchingHeader("User-Agent"))
89+
.hasValueSatisfying(userAgent -> assertThat(userAgent).contains("aws-sdk-java/2.").contains("ft/warmup"));
90+
}
91+
8092
@Test
8193
void warmAll_whenRequestFails_swallowsAndStillClosesClient() {
8294
SdkAsyncHttpClient client = mock(SdkAsyncHttpClient.class);

core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/loader/SyncHttpClientWarmerTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,18 @@ void warmAll_whenInvoked_issuesGetToResolvedEndpoint() {
7272
assertThat(request.getValue().httpRequest().getUri()).isEqualTo(ENDPOINT);
7373
}
7474

75+
@Test
76+
void warmAll_whenInvoked_addsWarmUpUserAgentHeader() {
77+
SdkHttpClient client = stubClient(respondingWith(403, emptyBody()));
78+
ArgumentCaptor<HttpExecuteRequest> request = ArgumentCaptor.forClass(HttpExecuteRequest.class);
79+
80+
warmer(serviceFor(client)).warmAll();
81+
82+
verify(client).prepareRequest(request.capture());
83+
assertThat(request.getValue().httpRequest().firstMatchingHeader("User-Agent"))
84+
.hasValueSatisfying(userAgent -> assertThat(userAgent).contains("aws-sdk-java/2.").contains("ft/warmup"));
85+
}
86+
7587
@Test
7688
void warmAll_whenRequestFails_swallowsAndStillClosesClient() throws IOException {
7789
SdkHttpClient client = mock(SdkHttpClient.class);

0 commit comments

Comments
 (0)