Skip to content

Commit

Permalink
fix: lowercase all headers in request serialization (#261)
Browse files Browse the repository at this point in the history
The HTTP request class doesn't have any implementations to insure
case-insensitivity of the http headers. So we need to be mindful
when populating these headers. Otherwise, the reqeust signature
will be messed up. This change will ensure the protocol-specific
default headers like content-type will be overriden by the serialized
header if exists.

For other headers added through middleware stack either by
customization or users, it wouldn't affect signing or sending as long
as the request doesn't contain same header names in different casing.
All the internal headers will be consistent. But users should be
careful when they are adding their own headers.

We don't add middleware to lowercase all headers to prevent
alternating the users' customizations.

Ref: aws/aws-sdk-js-v3#1800
  • Loading branch information
AllanZhengYP authored Jan 13, 2021
1 parent 10ba527 commit e92c8f6
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@
import software.amazon.smithy.protocoltests.traits.HttpResponseTestCase;
import software.amazon.smithy.protocoltests.traits.HttpResponseTestsTrait;
import software.amazon.smithy.utils.IoUtils;
import software.amazon.smithy.utils.ListUtils;
import software.amazon.smithy.utils.MapUtils;
import software.amazon.smithy.utils.Pair;

Expand All @@ -79,10 +78,6 @@ final class HttpProtocolTestGenerator implements Runnable {
private static final Logger LOGGER = Logger.getLogger(HttpProtocolTestGenerator.class.getName());
private static final String TEST_CASE_FILE_TEMPLATE = "tests/functional/%s.spec.ts";

// Headers that are generated by other sources that need to be normalized in the
// test cases to match.
private static final List<String> HEADERS_TO_NORMALIZE = ListUtils.of("content-length", "content-type");

private final TypeScriptSettings settings;
private final Model model;
private final ShapeId protocol;
Expand Down Expand Up @@ -255,30 +250,22 @@ private void writeRequestQueryAssertions(HttpRequestTestCase testCase) {

private void writeRequestHeaderAssertions(HttpRequestTestCase testCase) {
testCase.getRequireHeaders().forEach(requiredHeader -> {
writer.write("expect(r.headers[$S]).toBeDefined();", normalizeHeaderName(requiredHeader));
writer.write("expect(r.headers[$S]).toBeDefined();", requiredHeader.toLowerCase());
});
writer.write("");

testCase.getForbidHeaders().forEach(forbidHeader ->
writer.write("expect(r.headers[$S]).toBeUndefined();", normalizeHeaderName(forbidHeader)));
writer.write("expect(r.headers[$S]).toBeUndefined();", forbidHeader.toLowerCase()));
writer.write("");

testCase.getHeaders().forEach((header, value) -> {
header = normalizeHeaderName(header);
header = header.toLowerCase();
writer.write("expect(r.headers[$S]).toBeDefined();", header);
writer.write("expect(r.headers[$S]).toBe($S);", header, value);
});
writer.write("");
}

private String normalizeHeaderName(String headerName) {
if (HEADERS_TO_NORMALIZE.contains(headerName.toLowerCase())) {
return headerName.toLowerCase();
}

return headerName;
}

private void writeRequestBodyAssertions(OperationShape operation, HttpRequestTestCase testCase) {
testCase.getBody().ifPresent(body -> {
// If we expect an empty body, expect it to be falsy.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ private void writeHeaders(
String headerValue = getInputValue(context, binding.getLocation(), memberLocation + "!",
binding.getMember(), target);
writer.write("...isSerializableHeaderValue($L) && { $S: $L },",
memberLocation, binding.getLocationName(), headerValue);
memberLocation, binding.getLocationName().toLowerCase(Locale.US), headerValue);
}

// Handle assembling prefix headers.
Expand All @@ -376,15 +376,16 @@ private void writeHeaders(
// Iterate through each entry in the member.
writer.openBlock("...($1L !== undefined) && Object.keys($1L).reduce(", "),", memberLocation,
() -> {
writer.openBlock("(acc: any, suffix: string) => {", "}, {}",
() -> {
// Use a ! since we already validated the input member is defined above.
String headerValue = getInputValue(context, binding.getLocation(),
memberLocation + "![suffix]", binding.getMember(), target);
// Append the prefix to key.
writer.write("acc[$S + suffix] = $L;", binding.getLocationName(), headerValue);
writer.write("return acc;");
});
writer.openBlock("(acc: any, suffix: string) => ({", "}), {}",
() -> {
// Use a ! since we already validated the input member is defined above.
String headerValue = getInputValue(context, binding.getLocation(),
memberLocation + "![suffix]", binding.getMember(), target);
writer.write("...acc,");
// Append the prefix to key.
writer.write("[`$L$${suffix.toLowerCase()}`]: $L,",
binding.getLocationName().toLowerCase(Locale.US), headerValue);
});
}
);
}
Expand Down

0 comments on commit e92c8f6

Please sign in to comment.