Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
6 changes: 6 additions & 0 deletions .changes/next-release/bugfix-AWSSDKforJavav2-5e4425a.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "bugfix",
"category": "AWS SDK for Java v2",
"contributor": "",
"description": "Improve Sigv4 signing performance, particurally for rpc protocols"
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ public String getCanonicalRequestString() {
return canonicalRequestString;
}



private SortedMap<String, List<String>> canonicalQueryParams() {
if (canonicalParams == null) {
canonicalParams = getCanonicalQueryParams(request);
Expand Down Expand Up @@ -202,21 +204,16 @@ public static String getCanonicalHeadersString(List<Pair<String, List<String>>>
* Get the string representing which headers are part of the signing process. Header names are separated by a semicolon.
*/
public static String getSignedHeadersString(List<Pair<String, List<String>>> canonicalHeaders) {
String signedHeadersString;
StringBuilder headersString = new StringBuilder(512);
for (Pair<String, List<String>> header : canonicalHeaders) {
headersString.append(header.left()).append(";");
headersString.append(header.left()).append(';');
}
// get rid of trailing semicolon
signedHeadersString = headersString.toString();

boolean trimTrailingSemicolon = signedHeadersString.length() > 1 &&
signedHeadersString.endsWith(";");

if (trimTrailingSemicolon) {
signedHeadersString = signedHeadersString.substring(0, signedHeadersString.length() - 1);
if (headersString.length() > 0) {
headersString.setLength(headersString.length() - 1); // remove last semicolon
}
return signedHeadersString;

return headersString.toString();
}

/**
Expand Down Expand Up @@ -296,13 +293,17 @@ private static void addAndTrim(StringBuilder result, String value) {
* If the path is empty, a single-forward slash ('/') is returned.
*/
private static String getCanonicalUri(SdkHttpRequest request, Options options) {
String path = options.normalizePath ? request.getUri().normalize().getRawPath()
: request.encodedPath();

if (StringUtils.isEmpty(path)) {
String path = request.encodedPath();
if (StringUtils.isEmpty(path) || "/".equals(path)) {
return "/";
}

if (options.normalizePath) {
path = request.getUri().normalize().getRawPath();
}



if (options.doubleUrlEncode) {
path = SdkHttpUtils.urlEncodeIgnoreSlashes(path);
}
Expand All @@ -329,6 +330,10 @@ private static String getCanonicalUri(SdkHttpRequest request, Options options) {
* Get the sorted map of query parameters that are to be signed.
*/
private static SortedMap<String, List<String>> getCanonicalQueryParams(SdkHttpRequest request) {
if (request.numRawQueryParameters() == 0) {
return Collections.emptySortedMap();
}

SortedMap<String, List<String>> sorted = new TreeMap<>();

// Signing protocol expects the param values also to be sorted after url
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.awssdk.benchmark.signer;

import java.net.URI;
import java.time.Clock;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.http.auth.aws.internal.signer.CredentialScope;
import software.amazon.awssdk.http.auth.aws.internal.signer.V4Properties;
import software.amazon.awssdk.http.auth.aws.internal.signer.V4RequestSigner;
import software.amazon.awssdk.http.auth.aws.internal.signer.V4RequestSigningResult;
import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;


@State(Scope.Thread)
@Warmup(iterations = 3, time = 5, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 3, time = 10, timeUnit = TimeUnit.SECONDS)
@Fork(2)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class Sigv4SignerBenchmark {
SdkHttpRequest.Builder request;
V4RequestSigner siger;

// Setup runs once per trial or iteration (not measured)
@Setup(Level.Iteration)
public void setup() {
URI target = URI.create("https://test.com/");
request = SdkHttpRequest.builder()
.method(SdkHttpMethod.GET)
.uri(target)
.encodedPath(target.getPath())
.putHeader("x-amz-content-sha256", "checksum")
.putHeader("x-amz-archive-description", "test test");
AwsCredentialsIdentity creds =
AwsCredentialsIdentity.create("access", "secret");
Clock clock = Clock.systemUTC();
V4Properties properties = V4Properties.builder()
.credentials(creds)
.credentialScope(new CredentialScope("us-east-1", "demo", clock.instant()))
.signingClock(clock)
.doubleUrlEncode(true)
.normalizePath(true)
.build();

siger = V4RequestSigner.create(properties, "abc123");
}

@Benchmark
public void benchmarkSign(Blackhole blackhole) {
V4RequestSigningResult signingResult = siger.sign(request);
blackhole.consume(signingResult);
}
}
Loading