From f8f4d83434c87d48cad36e6c74216243ae9b55de Mon Sep 17 00:00:00 2001 From: Rob Cash Date: Tue, 1 Jul 2025 12:05:28 -0400 Subject: [PATCH 1/5] Update AwsSpringHttpProcessingUtils.java Corrected NullPointerException identified in bug 1504 --- .../proxy/spring/AwsSpringHttpProcessingUtils.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/AwsSpringHttpProcessingUtils.java b/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/AwsSpringHttpProcessingUtils.java index 0f6270b3..a5afe140 100644 --- a/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/AwsSpringHttpProcessingUtils.java +++ b/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/AwsSpringHttpProcessingUtils.java @@ -124,16 +124,18 @@ private static HttpServletRequest generateRequest1(String request, Context lambd populateQueryStringParametersV1(v1Request, httpRequest); populateMultiValueQueryStringParametersV1(v1Request, httpRequest); + String contentType = null; if (v1Request.getMultiValueHeaders() != null) { MultiValueMapAdapter headers = new MultiValueMapAdapter(v1Request.getMultiValueHeaders()); httpRequest.setHeaders(headers); + contentType = v1Request.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE); } - populateContentAndContentType( - v1Request.getBody(), - v1Request.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE), - v1Request.isBase64Encoded(), - httpRequest - ); + populateContentAndContentType( + v1Request.getBody(), + contentType, + v1Request.isBase64Encoded(), + httpRequest + ); if (v1Request.getRequestContext() != null) { httpRequest.setAttribute(RequestReader.API_GATEWAY_CONTEXT_PROPERTY, v1Request.getRequestContext()); httpRequest.setAttribute(RequestReader.ALB_CONTEXT_PROPERTY, v1Request.getRequestContext().getElb()); From f5b0710a13772b95f985da2177b532fe1aa037ad Mon Sep 17 00:00:00 2001 From: Rob Cash Date: Tue, 1 Jul 2025 13:18:51 -0400 Subject: [PATCH 2/5] Update AwsSpringHttpProcessingUtils.java Now copying single value headers to httpRequest when there are no multi-value headers present in the proxy request --- .../proxy/spring/AwsSpringHttpProcessingUtils.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/AwsSpringHttpProcessingUtils.java b/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/AwsSpringHttpProcessingUtils.java index a5afe140..64f31d0b 100644 --- a/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/AwsSpringHttpProcessingUtils.java +++ b/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/AwsSpringHttpProcessingUtils.java @@ -124,15 +124,19 @@ private static HttpServletRequest generateRequest1(String request, Context lambd populateQueryStringParametersV1(v1Request, httpRequest); populateMultiValueQueryStringParametersV1(v1Request, httpRequest); - String contentType = null; - if (v1Request.getMultiValueHeaders() != null) { + final boolean hasSVH = v1Request.getHeaders() != null && !v1Request.getHeaders().isEmpty(); + final boolean hasMVH = v1Request.getMultiValueHeaders() != null && !v1Request.getMultiValueHeaders().isEmpty(); + if (hasMVH) { MultiValueMapAdapter headers = new MultiValueMapAdapter(v1Request.getMultiValueHeaders()); httpRequest.setHeaders(headers); - contentType = v1Request.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE); + } + else if (hasSVH) + { + v1Request.getHeaders().forEach(httpRequest::addHeader); } populateContentAndContentType( v1Request.getBody(), - contentType, + httpRequest.getHeader(HttpHeaders.CONTENT_TYPE), v1Request.isBase64Encoded(), httpRequest ); From 52bbe3e74a7c7b1e64551df8d0fea31708e19949 Mon Sep 17 00:00:00 2001 From: Rob Cash Date: Tue, 1 Jul 2025 13:19:14 -0400 Subject: [PATCH 3/5] Update AwsSpringHttpProcessingUtilsTests.java Added unit test for when there are no multivalue headers --- .../AwsSpringHttpProcessingUtilsTests.java | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/AwsSpringHttpProcessingUtilsTests.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/AwsSpringHttpProcessingUtilsTests.java index 94232cbf..f5de7771 100644 --- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/AwsSpringHttpProcessingUtilsTests.java +++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/AwsSpringHttpProcessingUtilsTests.java @@ -115,6 +115,57 @@ public class AwsSpringHttpProcessingUtilsTests { + " \"isBase64Encoded\": false\n" + "}"; + private static String API_GATEWAY_EVENT_WITHOUT_MULTIVALUE_HEADERS = "{\n" + + " \"version\": \"1.0\",\n" + + " \"resource\": \"$default\",\n" + + " \"path\": \"/async\",\n" + + " \"httpMethod\": \"POST\",\n" + + " \"headers\": {\n" + + " \"Content-Length\": \"45\",\n" + + " \"Content-Type\": \"application/json\",\n" + + " \"Host\": \"i76bfh111.execute-api.eu-west-3.amazonaws.com\",\n" + + " \"User-Agent\": \"curl/7.79.1\",\n" + + " \"X-Amzn-Trace-Id\": \"Root=1-64087690-2151375b219d3ba3389ea84e\",\n" + + " \"X-Forwarded-For\": \"109.210.252.44\",\n" + + " \"X-Forwarded-Port\": \"443\",\n" + + " \"X-Forwarded-Proto\": \"https\",\n" + + " \"accept\": \"*/*\"\n" + + " },\n" + + " \"queryStringParameters\": {\n" + + " \"abc\": \"xyz\",\n" + + " \"parameter1\": \"value2\"\n" + + " },\n" + + " \"multiValueQueryStringParameters\": {\n" + + " \"abc\": [\n" + + " \"xyz\"\n" + + " ],\n" + + " \"parameter1\": [\n" + + " \"value1\",\n" + + " \"value2\"\n" + + " ]\n" + + " },\n" + + " \"requestContext\": {\n" + + " \"accountId\": \"123456789098\",\n" + + " \"apiId\": \"i76bfhczs0\",\n" + + " \"domainName\": \"i76bfhc111.execute-api.eu-west-3.amazonaws.com\",\n" + + " \"domainPrefix\": \"i76bfhczs0\",\n" + + " \"extendedRequestId\": \"Bdd2ngt5iGYEMIg=\",\n" + + " \"httpMethod\": \"POST\",\n" + + " \"path\": \"/pets\",\n" + + " \"protocol\": \"HTTP/1.1\",\n" + + " \"requestId\": \"Bdd2ngt5iGYEMIg=\",\n" + + " \"requestTime\": \"08/Mar/2023:11:50:40 +0000\",\n" + + " \"requestTimeEpoch\": 1678276240455,\n" + + " \"resourceId\": \"$default\",\n" + + " \"resourcePath\": \"$default\",\n" + + " \"stage\": \"$default\"\n" + + " },\n" + + " \"pathParameters\": null,\n" + + " \"stageVariables\": null,\n" + + " \"body\": \"{\\\"name\\\":\\\"bob\\\"}\",\n" + + " \"isBase64Encoded\": false\n" + + "}"; + private static String API_GATEWAY_EVENT_V2 = "{\n" + " \"version\": \"2.0\",\n" + " \"routeKey\": \"$default\",\n" + @@ -215,7 +266,7 @@ public class AwsSpringHttpProcessingUtilsTests { private final ObjectMapper mapper = new ObjectMapper(); public static Collection data() { - return Arrays.asList(new String[]{API_GATEWAY_EVENT, API_GATEWAY_EVENT_V2, ALB_EVENT}); + return Arrays.asList(new String[]{API_GATEWAY_EVENT, API_GATEWAY_EVENT_WITHOUT_MULTIVALUE_HEADERS, API_GATEWAY_EVENT_V2, ALB_EVENT}); } @MethodSource("data") From f9883d315382e615f42f05ff08f501406eb5759c Mon Sep 17 00:00:00 2001 From: Rob Cash Date: Mon, 21 Jul 2025 09:44:47 -0400 Subject: [PATCH 4/5] Update SpringDelegatingLambdaContainerHandlerTests.java Added V1 request without multi-value headers present --- ...DelegatingLambdaContainerHandlerTests.java | 77 ++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/SpringDelegatingLambdaContainerHandlerTests.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/SpringDelegatingLambdaContainerHandlerTests.java index 02ef21d9..7a578350 100644 --- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/SpringDelegatingLambdaContainerHandlerTests.java +++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/SpringDelegatingLambdaContainerHandlerTests.java @@ -122,6 +122,81 @@ public class SpringDelegatingLambdaContainerHandlerTests { + " \"isBase64Encoded\": false\n" + "}"; + /** + * NOTE: while API Gateway typically sets multiValueHeaders, even if they are only a copy of the single value + * headers, this is NOT the case when an API is invoked from the AWS console. In this situation, there are NO + * multivalue headers. + */ + private static String API_GATEWAY_EVENT_WITHOUT_MULTIVALUE_HEADERS = "{\n" + + " \"version\": \"1.0\",\n" + + " \"resource\": \"$default\",\n" + + " \"path\": \"/async\",\n" + + " \"httpMethod\": \"POST\",\n" + + " \"headers\": {\n" + + " \"Content-Length\": \"45\",\n" + + " \"Content-Type\": \"application/json\",\n" + + " \"Host\": \"i76bfh111.execute-api.eu-west-3.amazonaws.com\",\n" + + " \"User-Agent\": \"curl/7.79.1\",\n" + + " \"X-Amzn-Trace-Id\": \"Root=1-64087690-2151375b219d3ba3389ea84e\",\n" + + " \"X-Forwarded-For\": \"109.210.252.44\",\n" + + " \"X-Forwarded-Port\": \"443\",\n" + + " \"X-Forwarded-Proto\": \"https\",\n" + + " \"accept\": \"*/*\"\n" + + " },\n" + + " \"queryStringParameters\": {\n" + + " \"abc\": \"xyz\",\n" + + " \"name\": \"Ricky\",\n" + + " \"foo\": \"baz\"\n" + + " },\n" + + " \"multiValueQueryStringParameters\": {\n" + + " \"abc\": [\n" + + " \"xyz\"\n" + + " ],\n" + + " \"name\": [\n" + + " \"Ricky\"\n" + + " ],\n" + + " \"foo\": [\n" + + " \"bar\",\n" + + " \"baz\"\n" + + " ]\n" + + " },\n" + + " \"requestContext\": {\n" + + " \"accountId\": \"123456789098\",\n" + + " \"apiId\": \"i76bfhczs0\",\n" + + " \"domainName\": \"i76bfhc111.execute-api.eu-west-3.amazonaws.com\",\n" + + " \"domainPrefix\": \"i76bfhczs0\",\n" + + " \"extendedRequestId\": \"Bdd2ngt5iGYEMIg=\",\n" + + " \"httpMethod\": \"POST\",\n" + + " \"identity\": {\n" + + " \"accessKey\": null,\n" + + " \"accountId\": null,\n" + + " \"caller\": null,\n" + + " \"cognitoAmr\": null,\n" + + " \"cognitoAuthenticationProvider\": null,\n" + + " \"cognitoAuthenticationType\": null,\n" + + " \"cognitoIdentityId\": null,\n" + + " \"cognitoIdentityPoolId\": null,\n" + + " \"principalOrgId\": null,\n" + + " \"sourceIp\": \"109.210.252.44\",\n" + + " \"user\": null,\n" + + " \"userAgent\": \"curl/7.79.1\",\n" + + " \"userArn\": null\n" + + " },\n" + + " \"path\": \"/pets\",\n" + + " \"protocol\": \"HTTP/1.1\",\n" + + " \"requestId\": \"Bdd2ngt5iGYEMIg=\",\n" + + " \"requestTime\": \"08/Mar/2023:11:50:40 +0000\",\n" + + " \"requestTimeEpoch\": 1678276240455,\n" + + " \"resourceId\": \"$default\",\n" + + " \"resourcePath\": \"$default\",\n" + + " \"stage\": \"$default\"\n" + + " },\n" + + " \"pathParameters\": null,\n" + + " \"stageVariables\": null,\n" + + " \"body\": \"{\\\"name\\\":\\\"bob\\\"}\",\n" + + " \"isBase64Encoded\": false\n" + + "}"; + private static String API_GATEWAY_EVENT_V2 = "{\n" + " \"version\": \"2.0\",\n" + " \"routeKey\": \"$default\",\n" + @@ -202,7 +277,7 @@ public void initServletAppTest() throws ContainerInitializationException { } public static Collection data() { - return Arrays.asList(new String[]{API_GATEWAY_EVENT, API_GATEWAY_EVENT_V2}); + return Arrays.asList(new String[]{API_GATEWAY_EVENT, API_GATEWAY_EVENT_WITHOUT_MULTIVALUE_HEADERS, API_GATEWAY_EVENT_V2}); } @MethodSource("data") From b7caf3c808c95f71e638ed88a8aeea30ced966ad Mon Sep 17 00:00:00 2001 From: Rob Cash Date: Mon, 21 Jul 2025 09:48:55 -0400 Subject: [PATCH 5/5] Update AwsSpringHttpProcessingUtils.java Tweaked retrieval of ContentType header --- .../serverless/proxy/spring/AwsSpringHttpProcessingUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/AwsSpringHttpProcessingUtils.java b/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/AwsSpringHttpProcessingUtils.java index 64f31d0b..180e18bb 100644 --- a/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/AwsSpringHttpProcessingUtils.java +++ b/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/AwsSpringHttpProcessingUtils.java @@ -136,7 +136,7 @@ else if (hasSVH) } populateContentAndContentType( v1Request.getBody(), - httpRequest.getHeader(HttpHeaders.CONTENT_TYPE), + v1Request.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE), v1Request.isBase64Encoded(), httpRequest );