Skip to content

Commit c525c8c

Browse files
AllanZhengYPsrchase
authored andcommitted
fix: lowercase all headers in request serialization (smithy-lang#261)
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
1 parent 6b4a920 commit c525c8c

File tree

2 files changed

+14
-26
lines changed

2 files changed

+14
-26
lines changed

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/HttpProtocolTestGenerator.java

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@
5858
import software.amazon.smithy.protocoltests.traits.HttpResponseTestCase;
5959
import software.amazon.smithy.protocoltests.traits.HttpResponseTestsTrait;
6060
import software.amazon.smithy.utils.IoUtils;
61-
import software.amazon.smithy.utils.ListUtils;
6261
import software.amazon.smithy.utils.MapUtils;
6362
import software.amazon.smithy.utils.Pair;
6463

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

82-
// Headers that are generated by other sources that need to be normalized in the
83-
// test cases to match.
84-
private static final List<String> HEADERS_TO_NORMALIZE = ListUtils.of("content-length", "content-type");
85-
8681
private final TypeScriptSettings settings;
8782
private final Model model;
8883
private final ShapeId protocol;
@@ -255,30 +250,22 @@ private void writeRequestQueryAssertions(HttpRequestTestCase testCase) {
255250

256251
private void writeRequestHeaderAssertions(HttpRequestTestCase testCase) {
257252
testCase.getRequireHeaders().forEach(requiredHeader -> {
258-
writer.write("expect(r.headers[$S]).toBeDefined();", normalizeHeaderName(requiredHeader));
253+
writer.write("expect(r.headers[$S]).toBeDefined();", requiredHeader.toLowerCase());
259254
});
260255
writer.write("");
261256

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

266261
testCase.getHeaders().forEach((header, value) -> {
267-
header = normalizeHeaderName(header);
262+
header = header.toLowerCase();
268263
writer.write("expect(r.headers[$S]).toBeDefined();", header);
269264
writer.write("expect(r.headers[$S]).toBe($S);", header, value);
270265
});
271266
writer.write("");
272267
}
273268

274-
private String normalizeHeaderName(String headerName) {
275-
if (HEADERS_TO_NORMALIZE.contains(headerName.toLowerCase())) {
276-
return headerName.toLowerCase();
277-
}
278-
279-
return headerName;
280-
}
281-
282269
private void writeRequestBodyAssertions(OperationShape operation, HttpRequestTestCase testCase) {
283270
testCase.getBody().ifPresent(body -> {
284271
// If we expect an empty body, expect it to be falsy.

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/integration/HttpBindingProtocolGenerator.java

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ private void writeHeaders(
365365
String headerValue = getInputValue(context, binding.getLocation(), memberLocation + "!",
366366
binding.getMember(), target);
367367
writer.write("...isSerializableHeaderValue($L) && { $S: $L },",
368-
memberLocation, binding.getLocationName(), headerValue);
368+
memberLocation, binding.getLocationName().toLowerCase(Locale.US), headerValue);
369369
}
370370

371371
// Handle assembling prefix headers.
@@ -376,15 +376,16 @@ private void writeHeaders(
376376
// Iterate through each entry in the member.
377377
writer.openBlock("...($1L !== undefined) && Object.keys($1L).reduce(", "),", memberLocation,
378378
() -> {
379-
writer.openBlock("(acc: any, suffix: string) => {", "}, {}",
380-
() -> {
381-
// Use a ! since we already validated the input member is defined above.
382-
String headerValue = getInputValue(context, binding.getLocation(),
383-
memberLocation + "![suffix]", binding.getMember(), target);
384-
// Append the prefix to key.
385-
writer.write("acc[$S + suffix] = $L;", binding.getLocationName(), headerValue);
386-
writer.write("return acc;");
387-
});
379+
writer.openBlock("(acc: any, suffix: string) => ({", "}), {}",
380+
() -> {
381+
// Use a ! since we already validated the input member is defined above.
382+
String headerValue = getInputValue(context, binding.getLocation(),
383+
memberLocation + "![suffix]", binding.getMember(), target);
384+
writer.write("...acc,");
385+
// Append the prefix to key.
386+
writer.write("[`$L$${suffix.toLowerCase()}`]: $L,",
387+
binding.getLocationName().toLowerCase(Locale.US), headerValue);
388+
});
388389
}
389390
);
390391
}

0 commit comments

Comments
 (0)