Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,10 @@ private static Iterable<SdkAsyncHttpService> discoverServices() {
@Override
public void warmAll() {
URI endpoint = endpointProvider.get();
String userAgent = HttpClientWarmer.warmUpUserAgent();
WarmUpDiscovery.forEachDiscovered(services.iterator(), service -> {
SdkAsyncHttpClient client = service.createAsyncHttpClientFactory().buildWithDefaults(AttributeMap.empty());
warmClient(client, endpoint);
warmClient(client, endpoint, userAgent);
});
}

Expand All @@ -97,11 +98,12 @@ public void warmAll() {
* failure or timeout is logged and swallowed. We block on the execute future (bounded) because the bundled async
* clients complete it only after the body is drained, so its completion implies the full path was exercised.
*/
private void warmClient(SdkAsyncHttpClient client, URI endpoint) {
private void warmClient(SdkAsyncHttpClient client, URI endpoint, String userAgent) {
try {
SdkHttpFullRequest httpRequest = SdkHttpFullRequest.builder()
.method(SdkHttpMethod.GET)
.uri(endpoint)
.putHeader(HEADER_USER_AGENT, userAgent)
.build();
AsyncExecuteRequest request = AsyncExecuteRequest.builder()
.request(httpRequest)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,36 @@

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

import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.FEATURE_METADATA;
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.appendSpaceAndField;

import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.core.internal.useragent.SdkUserAgentBuilder;
import software.amazon.awssdk.core.util.SystemUserAgent;

/**
* Warms the sync or async HTTP clients on the classpath for CRaC priming.
*/
@SdkInternalApi
public interface HttpClientWarmer {

String HEADER_USER_AGENT = "User-Agent";

String WARM_UP_FEATURE_ID = "warmup";

/**
* Warms every HTTP client found on the classpath. Best-effort; never throws.
*/
void warmAll();

/**
* Builds the {@code User-Agent} header value for warm-up requests: the system user agent (SDK version, Java version, OS,
* etc.) plus the {@link #WARM_UP_FEATURE_ID} feature marker.
*/
static String warmUpUserAgent() {
StringBuilder uaString =
new StringBuilder(SdkUserAgentBuilder.buildSystemUserAgentString(SystemUserAgent.getOrCreate()));
appendSpaceAndField(uaString, FEATURE_METADATA, WARM_UP_FEATURE_ID);
return uaString.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,21 +79,23 @@ private static Iterable<SdkHttpService> discoverServices() {
@Override
public void warmAll() {
URI endpoint = endpointProvider.get();
String userAgent = HttpClientWarmer.warmUpUserAgent();
WarmUpDiscovery.forEachDiscovered(services.iterator(), service -> {
SdkHttpClient client = service.createHttpClientBuilder().buildWithDefaults(AttributeMap.empty());
warmClient(client, endpoint);
warmClient(client, endpoint, userAgent);
});
}

/**
* Sends the warm-up {@code GET} to {@code endpoint}, drains the response body, and closes the client. Best-effort: the
* goal is JIT compilation, not a successful request, so any failure is logged and swallowed.
*/
private void warmClient(SdkHttpClient client, URI endpoint) {
private void warmClient(SdkHttpClient client, URI endpoint, String userAgent) {
try {
SdkHttpRequest httpRequest = SdkHttpRequest.builder()
.method(SdkHttpMethod.GET)
.uri(endpoint)
.putHeader(HEADER_USER_AGENT, userAgent)
.build();
HttpExecuteRequest request = HttpExecuteRequest.builder()
.request(httpRequest)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ void warmAll_whenInvoked_issuesGetToResolvedEndpoint() {
assertThat(request.getValue().request().getUri()).isEqualTo(ENDPOINT);
}

@Test
void warmAll_whenInvoked_addsWarmUpUserAgentHeader() {
SdkAsyncHttpClient client = stubClient(emptyBody());
ArgumentCaptor<AsyncExecuteRequest> request = ArgumentCaptor.forClass(AsyncExecuteRequest.class);

warmer(serviceFor(client)).warmAll();

verify(client).execute(request.capture());
assertThat(request.getValue().request().firstMatchingHeader("User-Agent"))
.hasValueSatisfying(userAgent -> assertThat(userAgent).contains("aws-sdk-java/2.").contains("ft/warmup"));
}

@Test
void warmAll_whenRequestFails_swallowsAndStillClosesClient() {
SdkAsyncHttpClient client = mock(SdkAsyncHttpClient.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,18 @@ void warmAll_whenInvoked_issuesGetToResolvedEndpoint() {
assertThat(request.getValue().httpRequest().getUri()).isEqualTo(ENDPOINT);
}

@Test
void warmAll_whenInvoked_addsWarmUpUserAgentHeader() {
SdkHttpClient client = stubClient(respondingWith(403, emptyBody()));
ArgumentCaptor<HttpExecuteRequest> request = ArgumentCaptor.forClass(HttpExecuteRequest.class);

warmer(serviceFor(client)).warmAll();

verify(client).prepareRequest(request.capture());
assertThat(request.getValue().httpRequest().firstMatchingHeader("User-Agent"))
.hasValueSatisfying(userAgent -> assertThat(userAgent).contains("aws-sdk-java/2.").contains("ft/warmup"));
}

@Test
void warmAll_whenRequestFails_swallowsAndStillClosesClient() throws IOException {
SdkHttpClient client = mock(SdkHttpClient.class);
Expand Down
Loading