Skip to content

Commit 8a7efd1

Browse files
alextwoodsdavidh44
andauthored
Sigv4 Signer optimizations (#6383)
* Sigv4 Signer optimizations * Add changelog * Minor cleanup * Apply suggestions from code review Co-authored-by: David Ho <[email protected]> --------- Co-authored-by: David Ho <[email protected]>
1 parent 631a538 commit 8a7efd1

File tree

3 files changed

+104
-14
lines changed

3 files changed

+104
-14
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "bugfix",
3+
"category": "AWS SDK for Java v2",
4+
"contributor": "",
5+
"description": "Improve Sigv4 signing performance, particularly for RPC protocols"
6+
}

core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/internal/signer/V4CanonicalRequest.java

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -202,21 +202,16 @@ public static String getCanonicalHeadersString(List<Pair<String, List<String>>>
202202
* Get the string representing which headers are part of the signing process. Header names are separated by a semicolon.
203203
*/
204204
public static String getSignedHeadersString(List<Pair<String, List<String>>> canonicalHeaders) {
205-
String signedHeadersString;
206205
StringBuilder headersString = new StringBuilder(512);
207206
for (Pair<String, List<String>> header : canonicalHeaders) {
208-
headersString.append(header.left()).append(";");
207+
headersString.append(header.left()).append(';');
209208
}
210-
// get rid of trailing semicolon
211-
signedHeadersString = headersString.toString();
212209

213-
boolean trimTrailingSemicolon = signedHeadersString.length() > 1 &&
214-
signedHeadersString.endsWith(";");
215-
216-
if (trimTrailingSemicolon) {
217-
signedHeadersString = signedHeadersString.substring(0, signedHeadersString.length() - 1);
210+
if (headersString.length() > 0) {
211+
headersString.setLength(headersString.length() - 1); // remove last semicolon
218212
}
219-
return signedHeadersString;
213+
214+
return headersString.toString();
220215
}
221216

222217
/**
@@ -296,13 +291,17 @@ private static void addAndTrim(StringBuilder result, String value) {
296291
* If the path is empty, a single-forward slash ('/') is returned.
297292
*/
298293
private static String getCanonicalUri(SdkHttpRequest request, Options options) {
299-
String path = options.normalizePath ? request.getUri().normalize().getRawPath()
300-
: request.encodedPath();
301-
302-
if (StringUtils.isEmpty(path)) {
294+
String path = request.encodedPath();
295+
if (StringUtils.isEmpty(path) || "/".equals(path)) {
303296
return "/";
304297
}
305298

299+
if (options.normalizePath) {
300+
path = request.getUri().normalize().getRawPath();
301+
}
302+
303+
304+
306305
if (options.doubleUrlEncode) {
307306
path = SdkHttpUtils.urlEncodeIgnoreSlashes(path);
308307
}
@@ -329,6 +328,10 @@ private static String getCanonicalUri(SdkHttpRequest request, Options options) {
329328
* Get the sorted map of query parameters that are to be signed.
330329
*/
331330
private static SortedMap<String, List<String>> getCanonicalQueryParams(SdkHttpRequest request) {
331+
if (request.numRawQueryParameters() == 0) {
332+
return Collections.emptySortedMap();
333+
}
334+
332335
SortedMap<String, List<String>> sorted = new TreeMap<>();
333336

334337
// Signing protocol expects the param values also to be sorted after url
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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.benchmark.signer;
17+
18+
import java.net.URI;
19+
import java.time.Clock;
20+
import java.util.concurrent.TimeUnit;
21+
import org.openjdk.jmh.annotations.Benchmark;
22+
import org.openjdk.jmh.annotations.BenchmarkMode;
23+
import org.openjdk.jmh.annotations.Fork;
24+
import org.openjdk.jmh.annotations.Level;
25+
import org.openjdk.jmh.annotations.Measurement;
26+
import org.openjdk.jmh.annotations.Mode;
27+
import org.openjdk.jmh.annotations.OutputTimeUnit;
28+
import org.openjdk.jmh.annotations.Scope;
29+
import org.openjdk.jmh.annotations.Setup;
30+
import org.openjdk.jmh.annotations.State;
31+
import org.openjdk.jmh.annotations.Warmup;
32+
import org.openjdk.jmh.infra.Blackhole;
33+
import software.amazon.awssdk.http.SdkHttpMethod;
34+
import software.amazon.awssdk.http.SdkHttpRequest;
35+
import software.amazon.awssdk.http.auth.aws.internal.signer.CredentialScope;
36+
import software.amazon.awssdk.http.auth.aws.internal.signer.V4Properties;
37+
import software.amazon.awssdk.http.auth.aws.internal.signer.V4RequestSigner;
38+
import software.amazon.awssdk.http.auth.aws.internal.signer.V4RequestSigningResult;
39+
import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
40+
41+
42+
@State(Scope.Thread)
43+
@Warmup(iterations = 3, time = 5, timeUnit = TimeUnit.SECONDS)
44+
@Measurement(iterations = 3, time = 10, timeUnit = TimeUnit.SECONDS)
45+
@Fork(2)
46+
@BenchmarkMode(Mode.AverageTime)
47+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
48+
public class Sigv4SignerBenchmark {
49+
SdkHttpRequest.Builder request;
50+
V4RequestSigner siger;
51+
52+
// Setup runs once per trial or iteration (not measured)
53+
@Setup(Level.Iteration)
54+
public void setup() {
55+
URI target = URI.create("https://test.com/");
56+
request = SdkHttpRequest.builder()
57+
.method(SdkHttpMethod.GET)
58+
.uri(target)
59+
.encodedPath(target.getPath())
60+
.putHeader("x-amz-content-sha256", "checksum")
61+
.putHeader("x-amz-archive-description", "test test");
62+
AwsCredentialsIdentity creds =
63+
AwsCredentialsIdentity.create("access", "secret");
64+
Clock clock = Clock.systemUTC();
65+
V4Properties properties = V4Properties.builder()
66+
.credentials(creds)
67+
.credentialScope(new CredentialScope("us-east-1", "demo", clock.instant()))
68+
.signingClock(clock)
69+
.doubleUrlEncode(true)
70+
.normalizePath(true)
71+
.build();
72+
73+
siger = V4RequestSigner.create(properties, "abc123");
74+
}
75+
76+
@Benchmark
77+
public void benchmarkSign(Blackhole blackhole) {
78+
V4RequestSigningResult signingResult = siger.sign(request);
79+
blackhole.consume(signingResult);
80+
}
81+
}

0 commit comments

Comments
 (0)