Skip to content

Optimize AWS4Signer #3324

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jun 28, 2024
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
39 changes: 22 additions & 17 deletions sdk/src/Core/Amazon.Runtime/Internal/Auth/AWS4Signer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
using Amazon.Util;
using Amazon.Runtime.Internal.Util;
using Amazon.Runtime.Endpoints;
using ThirdParty.RuntimeBackports;

namespace Amazon.Runtime.Internal.Auth
{
Expand Down Expand Up @@ -818,22 +819,22 @@ private static string CanonicalizeRequestHelper(Uri endpoint,
IDictionary<string, string> pathResources,
bool doubleEncode)
{
var canonicalRequest = new StringBuilder();
canonicalRequest.AppendFormat("{0}\n", httpMethod);
canonicalRequest.AppendFormat("{0}\n", AWSSDKUtils.CanonicalizeResourcePathV2(endpoint, resourcePath, doubleEncode, pathResources));
canonicalRequest.AppendFormat("{0}\n", canonicalQueryString);
var canonicalRequest = new ValueStringBuilder(512);
canonicalRequest.Append(httpMethod);
canonicalRequest.Append('\n');
canonicalRequest.Append($"{AWSSDKUtils.CanonicalizeResourcePathV2(endpoint, resourcePath, doubleEncode, pathResources)}\n");
canonicalRequest.Append($"{canonicalQueryString}\n");

canonicalRequest.AppendFormat("{0}\n", CanonicalizeHeaders(sortedHeaders));
canonicalRequest.AppendFormat("{0}\n", CanonicalizeHeaderNames(sortedHeaders));
canonicalRequest.Append($"{CanonicalizeHeaders(sortedHeaders)}\n");
canonicalRequest.Append($"{CanonicalizeHeaderNames(sortedHeaders)}\n");

if (precomputedBodyHash != null)
{
canonicalRequest.Append(precomputedBodyHash);
}
else
{
string contentHash;
if (sortedHeaders.TryGetValue(HeaderKeys.XAmzContentSha256Header, out contentHash))
if (sortedHeaders.TryGetValue(HeaderKeys.XAmzContentSha256Header, out var contentHash))
canonicalRequest.Append(contentHash);
}

Expand Down Expand Up @@ -870,16 +871,20 @@ protected internal static IDictionary<string, string> SortAndPruneHeaders(IEnume
/// <returns>Canonicalized string of headers, with the header names in lower case.</returns>
protected internal static string CanonicalizeHeaders(IEnumerable<KeyValuePair<string, string>> sortedHeaders)
{
if (sortedHeaders == null || sortedHeaders.Count() == 0)
if (sortedHeaders == null)
return string.Empty;

var builder = new StringBuilder();

foreach (var entry in sortedHeaders)
// Majority of the cases we will always have a IDictionary<string, string> for headers which implements ICollection<KeyValuePair<string, string>>.
var materializedSortedHeaders = sortedHeaders as ICollection<KeyValuePair<string, string>> ?? sortedHeaders.ToList();
if (materializedSortedHeaders.Count == 0)
return string.Empty;

var builder = new ValueStringBuilder(512);
foreach (var entry in materializedSortedHeaders)
{
// Refer https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html. (Step #4: "To create the canonical headers list, convert all header names to lowercase and remove leading spaces and trailing spaces. Convert sequential spaces in the header value to a single space.").
builder.Append(entry.Key.ToLowerInvariant());
builder.Append(":");
builder.Append(':');
builder.Append(AWSSDKUtils.CompressSpaces(entry.Value)?.Trim());
builder.Append("\n");
}
Expand All @@ -893,15 +898,15 @@ protected internal static string CanonicalizeHeaders(IEnumerable<KeyValuePair<st
/// <returns>Formatted string of header names</returns>
protected static string CanonicalizeHeaderNames(IEnumerable<KeyValuePair<string, string>> sortedHeaders)
{
var builder = new StringBuilder();
var builder = new ValueStringBuilder(512);

foreach (var header in sortedHeaders)
{
if (builder.Length > 0)
builder.Append(";");
builder.Append(';');
builder.Append(header.Key.ToLowerInvariant());
}

return builder.ToString();
}

Expand Down
10 changes: 7 additions & 3 deletions sdk/src/Core/Amazon.Util/AWSSDKUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
using System.Reflection;
using System.Threading;
using Amazon.Runtime.Endpoints;
using ThirdParty.RuntimeBackports;

#if AWS_ASYNC_API
using System.Threading.Tasks;
Expand Down Expand Up @@ -1643,21 +1644,24 @@ public static string CompressSpaces(string data)
return null;
}

if (data.Length == 0)
var dataLength = data.Length;
if (dataLength == 0)
{
return string.Empty;
}

var stringBuilder = new StringBuilder();
var stringBuilder = new ValueStringBuilder(dataLength);
int index = 0;
var isWhiteSpace = false;
foreach (var character in data)
{
if (!isWhiteSpace | !(isWhiteSpace = char.IsWhiteSpace(character)))
{
stringBuilder.Append(isWhiteSpace ? ' ' : character);
index++;
}
}
return stringBuilder.ToString();
return stringBuilder.ToString(0, index);
}

/// <summary>
Expand Down
Loading