Skip to content

Commit 5a892e4

Browse files
committed
Various performance improvements.
1 parent be747d6 commit 5a892e4

File tree

13 files changed

+1000
-130
lines changed

13 files changed

+1000
-130
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "feature",
3+
"category": "AWS SDK for Java v2",
4+
"description": "Various performance improvements."
5+
}

core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/AbstractAws4Signer.java

Lines changed: 47 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.Arrays;
2525
import java.util.List;
2626
import java.util.Map;
27+
import java.util.TreeMap;
2728
import software.amazon.awssdk.annotations.SdkInternalApi;
2829
import software.amazon.awssdk.auth.credentials.AwsCredentials;
2930
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
@@ -75,7 +76,14 @@ protected SdkHttpFullRequest.Builder doSign(SdkHttpFullRequest request,
7576
.filter(h -> h.equals("required"))
7677
.ifPresent(h -> mutableRequest.putHeader(SignerConstant.X_AMZ_CONTENT_SHA256, contentSha256));
7778

78-
String canonicalRequest = createCanonicalRequest(mutableRequest, contentSha256, signingParams.doubleUrlEncode());
79+
Map<String, List<String>> canonicalHeaders = canonicalizeSigningHeaders(mutableRequest.headers());
80+
String signedHeadersString = getSignedHeadersString(canonicalHeaders);
81+
82+
String canonicalRequest = createCanonicalRequest(mutableRequest,
83+
canonicalHeaders,
84+
signedHeadersString,
85+
contentSha256,
86+
signingParams.doubleUrlEncode());
7987

8088
String stringToSign = createStringToSign(canonicalRequest, requestParams);
8189

@@ -84,7 +92,7 @@ protected SdkHttpFullRequest.Builder doSign(SdkHttpFullRequest request,
8492
byte[] signature = computeSignature(stringToSign, signingKey);
8593

8694
mutableRequest.putHeader(SignerConstant.AUTHORIZATION,
87-
buildAuthorizationHeader(signature, sanitizedCredentials, requestParams, mutableRequest));
95+
buildAuthorizationHeader(signature, sanitizedCredentials, requestParams, signedHeadersString));
8896

8997
processRequestPayload(mutableRequest, signature, signingKey, requestParams, signingParams);
9098

@@ -110,11 +118,16 @@ protected SdkHttpFullRequest.Builder doPresign(SdkHttpFullRequest request,
110118
}
111119

112120
// Add the important parameters for v4 signing
113-
addPreSignInformationToRequest(mutableRequest, sanitizedCredentials, requestParams, expirationInSeconds);
121+
Map<String, List<String>> canonicalizedHeaders = canonicalizeSigningHeaders(mutableRequest.headers());
122+
String signedHeadersString = getSignedHeadersString(canonicalizedHeaders);
123+
124+
addPreSignInformationToRequest(mutableRequest, signedHeadersString, sanitizedCredentials,
125+
requestParams, expirationInSeconds);
114126

115127
String contentSha256 = calculateContentHashPresign(mutableRequest, signingParams);
116128

117-
String canonicalRequest = createCanonicalRequest(mutableRequest, contentSha256, signingParams.doubleUrlEncode());
129+
String canonicalRequest = createCanonicalRequest(mutableRequest, canonicalizedHeaders, signedHeadersString,
130+
contentSha256, signingParams.doubleUrlEncode());
118131

119132
String stringToSign = createStringToSign(canonicalRequest, requestParams);
120133

@@ -191,19 +204,20 @@ protected final byte[] deriveSigningKey(AwsCredentials credentials, Instant sign
191204
* generate the canonical request.
192205
*/
193206
private String createCanonicalRequest(SdkHttpFullRequest.Builder request,
207+
Map<String, List<String>> canonicalHeaders,
208+
String signedHeadersString,
194209
String contentSha256,
195210
boolean doubleUrlEncode) {
196-
197211
String canonicalRequest = request.method().toString() +
198212
SignerConstant.LINE_SEPARATOR +
199213
// This would optionally double url-encode the resource path
200214
getCanonicalizedResourcePath(request.encodedPath(), doubleUrlEncode) +
201215
SignerConstant.LINE_SEPARATOR +
202216
getCanonicalizedQueryString(request.rawQueryParameters()) +
203217
SignerConstant.LINE_SEPARATOR +
204-
getCanonicalizedHeaderString(request.headers()) +
218+
getCanonicalizedHeaderString(canonicalHeaders) +
205219
SignerConstant.LINE_SEPARATOR +
206-
getSignedHeadersString(request.headers()) +
220+
signedHeadersString +
207221
SignerConstant.LINE_SEPARATOR +
208222
contentSha256;
209223

@@ -254,12 +268,11 @@ private byte[] computeSignature(String stringToSign, byte[] signingKey) {
254268
private String buildAuthorizationHeader(byte[] signature,
255269
AwsCredentials credentials,
256270
Aws4SignerRequestParams signerParams,
257-
SdkHttpFullRequest.Builder mutableRequest) {
271+
String signedHeadersString) {
258272

259273
String signingCredentials = credentials.accessKeyId() + "/" + signerParams.getScope();
260274
String credential = "Credential=" + signingCredentials;
261-
String signerHeaders = "SignedHeaders=" +
262-
getSignedHeadersString(mutableRequest.headers());
275+
String signerHeaders = "SignedHeaders=" + signedHeadersString;
263276
String signatureHeader = "Signature=" + BinaryUtils.toHex(signature);
264277

265278
return SignerConstant.AWS4_SIGNING_ALGORITHM + " " + credential + ", " + signerHeaders + ", " + signatureHeader;
@@ -269,6 +282,7 @@ private String buildAuthorizationHeader(byte[] signature,
269282
* Includes all the signing headers as request parameters for pre-signing.
270283
*/
271284
private void addPreSignInformationToRequest(SdkHttpFullRequest.Builder mutableRequest,
285+
String signedHeadersString,
272286
AwsCredentials sanitizedCredentials,
273287
Aws4SignerRequestParams signerParams,
274288
long expirationInSeconds) {
@@ -277,34 +291,39 @@ private void addPreSignInformationToRequest(SdkHttpFullRequest.Builder mutableRe
277291

278292
mutableRequest.putRawQueryParameter(SignerConstant.X_AMZ_ALGORITHM, SignerConstant.AWS4_SIGNING_ALGORITHM);
279293
mutableRequest.putRawQueryParameter(SignerConstant.X_AMZ_DATE, signerParams.getFormattedRequestSigningDateTime());
280-
mutableRequest.putRawQueryParameter(SignerConstant.X_AMZ_SIGNED_HEADER,
281-
getSignedHeadersString(mutableRequest.headers()));
282-
mutableRequest.putRawQueryParameter(SignerConstant.X_AMZ_EXPIRES,
283-
Long.toString(expirationInSeconds));
294+
mutableRequest.putRawQueryParameter(SignerConstant.X_AMZ_SIGNED_HEADER, signedHeadersString);
295+
mutableRequest.putRawQueryParameter(SignerConstant.X_AMZ_EXPIRES, Long.toString(expirationInSeconds));
284296
mutableRequest.putRawQueryParameter(SignerConstant.X_AMZ_CREDENTIAL, signingCredentials);
285297
}
286298

299+
private Map<String, List<String>> canonicalizeSigningHeaders(Map<String, List<String>> headers) {
300+
Map<String, List<String>> result = new TreeMap<>();
287301

288-
private String getCanonicalizedHeaderString(Map<String, List<String>> headers) {
289-
List<String> sortedHeaders = new ArrayList<>(headers.keySet());
290-
sortedHeaders.sort(String.CASE_INSENSITIVE_ORDER);
291-
292-
StringBuilder buffer = new StringBuilder();
293-
for (String header : sortedHeaders) {
294-
if (shouldExcludeHeaderFromSigning(header)) {
302+
for (Map.Entry<String, List<String>> header : headers.entrySet()) {
303+
String lowerCaseHeader = lowerCase(header.getKey());
304+
if (LIST_OF_HEADERS_TO_IGNORE_IN_LOWER_CASE.contains(lowerCaseHeader)) {
295305
continue;
296306
}
297-
String key = lowerCase(header);
298307

299-
for (String headerValue : headers.get(header)) {
300-
appendCompactedString(buffer, key);
308+
result.computeIfAbsent(lowerCaseHeader, x -> new ArrayList<>()).addAll(header.getValue());
309+
}
310+
311+
return result;
312+
}
313+
314+
private String getCanonicalizedHeaderString(Map<String, List<String>> canonicalizedHeaders) {
315+
StringBuilder buffer = new StringBuilder();
316+
317+
canonicalizedHeaders.forEach((headerName, headerValues) -> {
318+
for (String headerValue : headerValues) {
319+
appendCompactedString(buffer, headerName);
301320
buffer.append(":");
302321
if (headerValue != null) {
303322
appendCompactedString(buffer, headerValue);
304323
}
305324
buffer.append("\n");
306325
}
307-
}
326+
});
308327

309328
return buffer.toString();
310329
}
@@ -350,28 +369,17 @@ private boolean isWhiteSpace(final char ch) {
350369
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\u000b' || ch == '\r' || ch == '\f';
351370
}
352371

353-
private String getSignedHeadersString(Map<String, List<String>> headers) {
354-
List<String> sortedHeaders = new ArrayList<>(headers.keySet());
355-
sortedHeaders.sort(String.CASE_INSENSITIVE_ORDER);
356-
372+
private String getSignedHeadersString(Map<String, List<String>> canonicalizedHeaders) {
357373
StringBuilder buffer = new StringBuilder();
358-
for (String header : sortedHeaders) {
359-
if (shouldExcludeHeaderFromSigning(header)) {
360-
continue;
361-
}
374+
for (String header : canonicalizedHeaders.keySet()) {
362375
if (buffer.length() > 0) {
363376
buffer.append(";");
364377
}
365-
buffer.append(lowerCase(header));
378+
buffer.append(header);
366379
}
367-
368380
return buffer.toString();
369381
}
370382

371-
private boolean shouldExcludeHeaderFromSigning(String header) {
372-
return LIST_OF_HEADERS_TO_IGNORE_IN_LOWER_CASE.contains(lowerCase(header));
373-
}
374-
375383
private void addHostHeader(SdkHttpFullRequest.Builder mutableRequest) {
376384
// AWS4 requires that we sign the Host header so we
377385
// have to have it in the request by the time we sign.

core/sdk-core/src/main/java/software/amazon/awssdk/core/RequestOverrideConfiguration.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ default B putRawQueryParameter(String name, String value) {
348348
}
349349

350350
protected abstract static class BuilderImpl<B extends Builder> implements Builder<B> {
351-
private Map<String, List<String>> headers = new HashMap<>();
351+
private Map<String, List<String>> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
352352
private Map<String, List<String>> rawQueryParameters = new HashMap<>();
353353
private List<ApiName> apiNames = new ArrayList<>();
354354
private Duration apiCallTimeout;
@@ -366,7 +366,7 @@ protected BuilderImpl(RequestOverrideConfiguration sdkRequestOverrideConfig) {
366366

367367
@Override
368368
public Map<String, List<String>> headers() {
369-
return CollectionUtils.deepUnmodifiableMap(headers, () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER));
369+
return CollectionUtils.unmodifiableMapOfLists(headers);
370370
}
371371

372372
@Override
@@ -386,7 +386,7 @@ public B headers(Map<String, List<String>> headers) {
386386

387387
@Override
388388
public Map<String, List<String>> rawQueryParameters() {
389-
return CollectionUtils.deepUnmodifiableMap(rawQueryParameters);
389+
return CollectionUtils.unmodifiableMapOfLists(rawQueryParameters);
390390
}
391391

392392
@Override

core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ public void setHeaders(Map<String, List<String>> additionalHttpHeaders) {
436436

437437
@Override
438438
public Map<String, List<String>> headers() {
439-
return CollectionUtils.deepUnmodifiableMap(headers);
439+
return CollectionUtils.unmodifiableMapOfLists(headers);
440440
}
441441

442442
@Override

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/utils/RetryableStageHelper.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,11 @@ public SdkHttpFullRequest requestToSend() {
138138
Integer availableRetryCapacity = TokenBucketRetryCondition.getCapacityForExecution(context.executionAttributes())
139139
.map(TokenBucketRetryCondition.Capacity::capacityRemaining)
140140
.orElse(null);
141-
141+
String headerValue = (attemptNumber - 1) + "/" +
142+
lastBackoffDelay.toMillis() + "/" +
143+
(availableRetryCapacity != null ? availableRetryCapacity : "");
142144
return request.toBuilder()
143-
.putHeader(SDK_RETRY_INFO_HEADER,
144-
String.format("%s/%s/%s",
145-
attemptNumber - 1,
146-
lastBackoffDelay.toMillis(),
147-
availableRetryCapacity != null ? availableRetryCapacity : ""))
145+
.putHeader(SDK_RETRY_INFO_HEADER, headerValue)
148146
.build();
149147
}
150148

0 commit comments

Comments
 (0)