-
Notifications
You must be signed in to change notification settings - Fork 937
Correctly handle multi-value headers in Aws4Signer #2629
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"category": "AWS SDK for Java v2", | ||
"contributor": "", | ||
"type": "bugfix", | ||
"description": "Correctly handle multi-value headers in Aws4Signer" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,7 @@ | |
import java.util.List; | ||
import java.util.Map; | ||
import java.util.TreeMap; | ||
import java.util.stream.Collectors; | ||
import software.amazon.awssdk.annotations.SdkInternalApi; | ||
import software.amazon.awssdk.auth.credentials.AwsCredentials; | ||
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials; | ||
|
@@ -323,46 +324,51 @@ private String getCanonicalizedHeaderString(Map<String, List<String>> canonicali | |
StringBuilder buffer = new StringBuilder(); | ||
|
||
canonicalizedHeaders.forEach((headerName, headerValues) -> { | ||
for (String headerValue : headerValues) { | ||
appendCompactedString(buffer, headerName); | ||
buffer.append(":"); | ||
if (headerValue != null) { | ||
appendCompactedString(buffer, headerValue); | ||
} | ||
buffer.append("\n"); | ||
} | ||
buffer.append(headerName); | ||
buffer.append(":"); | ||
buffer.append(String.join(",", trimAll(headerValues))); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again This is doing some extra memory allocations for the |
||
buffer.append("\n"); | ||
}); | ||
|
||
return buffer.toString(); | ||
} | ||
|
||
/** | ||
* This method appends a string to a string builder and collapses contiguous | ||
* white space is a single space. | ||
* | ||
* This is equivalent to: | ||
* destination.append(source.replaceAll("\\s+", " ")) | ||
* "The Trimall function removes excess white space before and after values, | ||
* and converts sequential spaces to a single space." | ||
* <p> | ||
* https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html | ||
* <p> | ||
* The collapse-whitespace logic is equivalent to: | ||
* <pre> | ||
* value.replaceAll("\\s+", " ") | ||
* </pre> | ||
* but does not create a Pattern object that needs to compile the match | ||
* string; it also prevents us from having to make a Matcher object as well. | ||
* | ||
*/ | ||
private void appendCompactedString(final StringBuilder destination, final String source) { | ||
private String trimAll(String value) { | ||
boolean previousIsWhiteSpace = false; | ||
int length = source.length(); | ||
StringBuilder sb = new StringBuilder(value.length()); | ||
|
||
for (int i = 0; i < length; i++) { | ||
char ch = source.charAt(i); | ||
for (int i = 0; i < value.length(); i++) { | ||
char ch = value.charAt(i); | ||
if (isWhiteSpace(ch)) { | ||
if (previousIsWhiteSpace) { | ||
continue; | ||
} | ||
destination.append(' '); | ||
sb.append(' '); | ||
previousIsWhiteSpace = true; | ||
} else { | ||
destination.append(ch); | ||
sb.append(ch); | ||
previousIsWhiteSpace = false; | ||
} | ||
} | ||
|
||
return sb.toString().trim(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See 632fea8 for a version that doesn't require allocating extra strings. I'd prefer that rather than allocating all these extra objects. They are short lived objects and probably won't make a huge different--but also this code is used by so many projects may be even tiny instructions and memory savings is worth it. |
||
} | ||
|
||
private List<String> trimAll(List<String> values) { | ||
return values.stream().map(this::trimAll).collect(Collectors.toList()); | ||
} | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: Use primitive char type ':' instead of String ":" type. String's generally have a large overhead. The same goes for all places where String literals are used in this function. Probably doesn't really make a huge different or may be the java compiler does this optimization already for us, but I'd prefer it that way.