From df70d560e4a8def8fd1781657e176ad2882a7701 Mon Sep 17 00:00:00 2001 From: mbfreder Date: Sun, 8 Oct 2023 22:33:54 -0700 Subject: [PATCH 1/8] Added java-events v4 dependency --- aws-serverless-java-container-core/pom.xml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/aws-serverless-java-container-core/pom.xml b/aws-serverless-java-container-core/pom.xml index 5d23fcbf6..02a5284c6 100644 --- a/aws-serverless-java-container-core/pom.xml +++ b/aws-serverless-java-container-core/pom.xml @@ -63,6 +63,25 @@ 6.1.4 test + + com.amazonaws + aws-lambda-java-events + 4.0.0 + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-core + + + From ebd5acdbe88c4cd8134d73261fad94593d8e9d87 Mon Sep 17 00:00:00 2001 From: mbfreder Date: Mon, 9 Oct 2023 01:14:45 -0700 Subject: [PATCH 2/8] migrate to APIGatewayV2HTTPEvent --- .../AwsHttpApiV2SecurityContextWriter.java | 6 +- .../jaxrs/AwsHttpApiV2SecurityContext.java | 12 +- .../ApacheCombinedServletLogFormatter.java | 4 +- .../AwsHttpApiV2HttpServletRequestReader.java | 10 +- .../AwsHttpApiV2ProxyHttpServletRequest.java | 10 +- .../servlet/AwsProxyRequestDispatcher.java | 6 +- .../ServletLambdaContainerHandlerBuilder.java | 4 +- .../proxy/model/HttpApiV2AuthorizerMap.java | 103 --------- .../proxy/model/HttpApiV2HttpContext.java | 61 ----- .../proxy/model/HttpApiV2JwtAuthorizer.java | 37 --- .../proxy/model/HttpApiV2ProxyRequest.java | 141 ------------ .../model/HttpApiV2ProxyRequestContext.java | 130 ----------- .../jaxrs/HttpApiV2SecurityContextTest.java | 8 +- ...HttpApiV2HttpServletRequestReaderTest.java | 9 +- .../testutils/AwsProxyRequestBuilder.java | 21 +- .../model/HttpApiV2ProxyRequestTest.java | 210 ------------------ .../jersey/JerseyLambdaContainerHandler.java | 8 +- .../proxy/jersey/JerseyAwsProxyTest.java | 4 +- .../proxy/jersey/JerseyParamEncodingTest.java | 4 +- .../spring/SpringLambdaContainerHandler.java | 6 +- .../proxy/spring/SpringAwsProxyTest.java | 3 +- .../CustomSpringLambdaContainerHandler.java | 6 +- .../pom.xml | 8 + .../SpringBootLambdaContainerHandler.java | 7 +- ...pringDelegatingLambdaContainerHandler.java | 4 +- .../spring/servletapp/LambdaHandler.java | 6 +- .../servletapp/LambdaStreamHandler.java | 8 +- .../spring/webfluxapp/LambdaHandler.java | 6 +- 28 files changed, 83 insertions(+), 759 deletions(-) delete mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/HttpApiV2AuthorizerMap.java delete mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/HttpApiV2HttpContext.java delete mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/HttpApiV2JwtAuthorizer.java delete mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/HttpApiV2ProxyRequest.java delete mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/HttpApiV2ProxyRequestContext.java delete mode 100644 aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/HttpApiV2ProxyRequestTest.java diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/AwsHttpApiV2SecurityContextWriter.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/AwsHttpApiV2SecurityContextWriter.java index d4192141a..8d1c3f6ee 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/AwsHttpApiV2SecurityContextWriter.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/AwsHttpApiV2SecurityContextWriter.java @@ -13,14 +13,14 @@ package com.amazonaws.serverless.proxy; import com.amazonaws.serverless.proxy.internal.jaxrs.AwsHttpApiV2SecurityContext; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import jakarta.ws.rs.core.SecurityContext; -public class AwsHttpApiV2SecurityContextWriter implements SecurityContextWriter { +public class AwsHttpApiV2SecurityContextWriter implements SecurityContextWriter { @Override - public SecurityContext writeSecurityContext(HttpApiV2ProxyRequest event, Context lambdaContext) { + public SecurityContext writeSecurityContext(APIGatewayV2HTTPEvent event, Context lambdaContext) { return new AwsHttpApiV2SecurityContext(lambdaContext, event); } } diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/jaxrs/AwsHttpApiV2SecurityContext.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/jaxrs/AwsHttpApiV2SecurityContext.java index f0e1f963d..329b39574 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/jaxrs/AwsHttpApiV2SecurityContext.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/jaxrs/AwsHttpApiV2SecurityContext.java @@ -14,8 +14,8 @@ import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; import com.amazonaws.serverless.proxy.internal.SecurityUtils; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import org.slf4j.Logger; @@ -33,9 +33,9 @@ public class AwsHttpApiV2SecurityContext implements SecurityContext { private static Logger log = LoggerFactory.getLogger(AwsHttpApiV2SecurityContext.class); private Context lambdaContext; - private HttpApiV2ProxyRequest event; + private APIGatewayV2HTTPEvent event; - public AwsHttpApiV2SecurityContext(final Context lambdaCtx, final HttpApiV2ProxyRequest request) { + public AwsHttpApiV2SecurityContext(final Context lambdaCtx, final APIGatewayV2HTTPEvent request) { lambdaContext = lambdaCtx; event = request; } @@ -79,8 +79,8 @@ public boolean isUserInRole(String s) { return false; } - return event.getRequestContext().getAuthorizer().getJwtAuthorizer().getScopes().contains(s) || - event.getRequestContext().getAuthorizer().getJwtAuthorizer().getClaims().containsKey(s); + return event.getRequestContext().getAuthorizer().getJwt().getScopes().contains(s) || + event.getRequestContext().getAuthorizer().getJwt().getClaims().containsKey(s); } @@ -94,7 +94,7 @@ public String getAuthenticationScheme() { if (event.getRequestContext().getAuthorizer() == null) { return null; } - if (event.getRequestContext().getAuthorizer().isJwt()) { + if (event.getRequestContext().getAuthorizer().getJwt() != null) { return AUTH_SCHEME_JWT; } return null; diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ApacheCombinedServletLogFormatter.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ApacheCombinedServletLogFormatter.java index e0c7a7357..b460ae133 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ApacheCombinedServletLogFormatter.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ApacheCombinedServletLogFormatter.java @@ -15,7 +15,7 @@ import com.amazonaws.serverless.proxy.LogFormatter; import com.amazonaws.serverless.proxy.model.AwsProxyRequestContext; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequestContext; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import jakarta.servlet.http.HttpServletRequest; @@ -79,7 +79,7 @@ public String format(ContainerRequestType servletRequest, ContainerResponseType //LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" combined StringBuilder logLineBuilder = new StringBuilder(); AwsProxyRequestContext gatewayContext = (AwsProxyRequestContext)servletRequest.getAttribute(API_GATEWAY_CONTEXT_PROPERTY); - HttpApiV2ProxyRequestContext httpApiContext = (HttpApiV2ProxyRequestContext)servletRequest.getAttribute(HTTP_API_CONTEXT_PROPERTY); + APIGatewayV2HTTPEvent.RequestContext httpApiContext = (APIGatewayV2HTTPEvent.RequestContext)servletRequest.getAttribute(HTTP_API_CONTEXT_PROPERTY); // %h logLineBuilder.append(servletRequest.getRemoteAddr()); diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpApiV2HttpServletRequestReader.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpApiV2HttpServletRequestReader.java index c40740c96..ca993e479 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpApiV2HttpServletRequestReader.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpApiV2HttpServletRequestReader.java @@ -15,17 +15,17 @@ import com.amazonaws.serverless.exceptions.InvalidRequestEventException; import com.amazonaws.serverless.proxy.RequestReader; import com.amazonaws.serverless.proxy.model.ContainerConfig; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.core.SecurityContext; -public class AwsHttpApiV2HttpServletRequestReader extends RequestReader { +public class AwsHttpApiV2HttpServletRequestReader extends RequestReader { static final String INVALID_REQUEST_ERROR = "The incoming event is not a valid HTTP API v2 proxy request"; @Override - public HttpServletRequest readRequest(HttpApiV2ProxyRequest request, SecurityContext securityContext, Context lambdaContext, ContainerConfig config) throws InvalidRequestEventException { + public HttpServletRequest readRequest(APIGatewayV2HTTPEvent request, SecurityContext securityContext, Context lambdaContext, ContainerConfig config) throws InvalidRequestEventException { if (request.getRequestContext() == null || request.getRequestContext().getHttp().getMethod() == null || request.getRequestContext().getHttp().getMethod().equals("")) { throw new InvalidRequestEventException(INVALID_REQUEST_ERROR); } @@ -44,7 +44,7 @@ public HttpServletRequest readRequest(HttpApiV2ProxyRequest request, SecurityCon } @Override - protected Class getRequestClass() { - return HttpApiV2ProxyRequest.class; + protected Class getRequestClass() { + return APIGatewayV2HTTPEvent.class; } } diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpApiV2ProxyHttpServletRequest.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpApiV2ProxyHttpServletRequest.java index bdf11b819..fd1815089 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpApiV2ProxyHttpServletRequest.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpApiV2ProxyHttpServletRequest.java @@ -16,9 +16,9 @@ import com.amazonaws.serverless.proxy.internal.SecurityUtils; import com.amazonaws.serverless.proxy.model.ContainerConfig; import com.amazonaws.serverless.proxy.model.Headers; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest; import com.amazonaws.serverless.proxy.model.MultiValuedTreeMap; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,7 +42,7 @@ public class AwsHttpApiV2ProxyHttpServletRequest extends AwsHttpServletRequest { private static Logger log = LoggerFactory.getLogger(AwsHttpApiV2ProxyHttpServletRequest.class); - private HttpApiV2ProxyRequest request; + private APIGatewayV2HTTPEvent request; private MultiValuedTreeMap queryString; private Headers headers; private ContainerConfig config; @@ -55,7 +55,7 @@ public class AwsHttpApiV2ProxyHttpServletRequest extends AwsHttpServletRequest { * * @param lambdaContext The Lambda function context. This object is used for utility methods such as log */ - public AwsHttpApiV2ProxyHttpServletRequest(HttpApiV2ProxyRequest req, Context lambdaContext, SecurityContext sc, ContainerConfig cfg) { + public AwsHttpApiV2ProxyHttpServletRequest(APIGatewayV2HTTPEvent req, Context lambdaContext, SecurityContext sc, ContainerConfig cfg) { super(lambdaContext); request = req; config = cfg; @@ -64,7 +64,7 @@ public AwsHttpApiV2ProxyHttpServletRequest(HttpApiV2ProxyRequest req, Context la headers = headersMapToMultiValue(request.getHeaders()); } - public HttpApiV2ProxyRequest getRequest() { + public APIGatewayV2HTTPEvent getRequest() { return request; } @@ -380,7 +380,7 @@ public int getServerPort() { @Override public ServletInputStream getInputStream() throws IOException { if (requestInputStream == null) { - requestInputStream = new AwsServletInputStream(bodyStringToInputStream(request.getBody(), request.isBase64Encoded())); + requestInputStream = new AwsServletInputStream(bodyStringToInputStream(request.getBody(), request.getIsBase64Encoded())); } return requestInputStream; } diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyRequestDispatcher.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyRequestDispatcher.java index f314ba0d0..c84424b05 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyRequestDispatcher.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyRequestDispatcher.java @@ -14,7 +14,7 @@ import com.amazonaws.serverless.proxy.internal.SecurityUtils; import com.amazonaws.serverless.proxy.model.AwsProxyRequest; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -137,8 +137,8 @@ void setRequestPath(ServletRequest req, final String destinationPath) { ((AwsProxyRequest)req.getAttribute(API_GATEWAY_EVENT_PROPERTY)).setPath(dispatchTo); return; } - if (req.getAttribute(HTTP_API_EVENT_PROPERTY) != null && req.getAttribute(HTTP_API_EVENT_PROPERTY) instanceof HttpApiV2ProxyRequest) { - ((HttpApiV2ProxyRequest)req.getAttribute(HTTP_API_EVENT_PROPERTY)).setRawPath(destinationPath); + if (req.getAttribute(HTTP_API_EVENT_PROPERTY) != null && req.getAttribute(HTTP_API_EVENT_PROPERTY) instanceof APIGatewayV2HTTPEvent) { + ((APIGatewayV2HTTPEvent)req.getAttribute(HTTP_API_EVENT_PROPERTY)).setRawPath(destinationPath); return; } diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ServletLambdaContainerHandlerBuilder.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ServletLambdaContainerHandlerBuilder.java index b44ec7f26..fe44ccfc4 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ServletLambdaContainerHandlerBuilder.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ServletLambdaContainerHandlerBuilder.java @@ -16,8 +16,8 @@ import com.amazonaws.serverless.proxy.*; import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.List; @@ -113,7 +113,7 @@ public Builder defaultHttpApiV2Proxy() { .responseWriter((ResponseWriter) new AwsProxyHttpServletResponseWriter(true)) .securityContextWriter((SecurityContextWriter) new AwsHttpApiV2SecurityContextWriter()) .exceptionHandler((ExceptionHandler) new AwsProxyExceptionHandler()) - .requestTypeClass((Class) HttpApiV2ProxyRequest.class) + .requestTypeClass((Class) APIGatewayV2HTTPEvent.class) .responseTypeClass((Class) AwsProxyResponse.class); return self(); diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/HttpApiV2AuthorizerMap.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/HttpApiV2AuthorizerMap.java deleted file mode 100644 index 4fff028c4..000000000 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/HttpApiV2AuthorizerMap.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance - * with the License. A copy of the License is located at - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES - * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ -package com.amazonaws.serverless.proxy.model; - -import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -@JsonSerialize(using = HttpApiV2AuthorizerMap.HttpApiV2AuthorizerSerializer.class) -@JsonDeserialize(using = HttpApiV2AuthorizerMap.HttpApiV2AuthorizerDeserializer.class) -public class HttpApiV2AuthorizerMap extends HashMap { - private static final String JWT_KEY = "jwt"; - private static final String LAMBDA_KEY = "lambda"; - private static final long serialVersionUID = 42L; - - public HttpApiV2JwtAuthorizer getJwtAuthorizer() { - return (HttpApiV2JwtAuthorizer)get(JWT_KEY); - } - - public Map getLambdaAuthorizerContext() { - return (Map) get(LAMBDA_KEY); - } - - public boolean isJwt() { - return containsKey(JWT_KEY); - } - - public boolean isLambda() { - return containsKey(LAMBDA_KEY); - } - - public void putJwtAuthorizer(HttpApiV2JwtAuthorizer jwt) { - put(JWT_KEY, jwt); - } - - public static class HttpApiV2AuthorizerDeserializer extends StdDeserializer { - private static final long serialVersionUID = 42L; - - public HttpApiV2AuthorizerDeserializer() { - super(HttpApiV2AuthorizerMap.class); - } - - @Override - public HttpApiV2AuthorizerMap deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { - HttpApiV2AuthorizerMap map = new HttpApiV2AuthorizerMap(); - JsonNode node = jsonParser.getCodec().readTree(jsonParser); - if (node.has(JWT_KEY)) { - HttpApiV2JwtAuthorizer authorizer = LambdaContainerHandler.getObjectMapper().treeToValue(node.get(JWT_KEY), HttpApiV2JwtAuthorizer.class); - map.putJwtAuthorizer(authorizer); - } - if (node.has(LAMBDA_KEY)) { - Map context = LambdaContainerHandler.getObjectMapper().treeToValue(node.get(LAMBDA_KEY), - TypeFactory.defaultInstance().constructMapType(HashMap.class, String.class, Object.class)); - map.put(LAMBDA_KEY, context); - } - // we ignore other, unknown values - return map; - } - } - - public static class HttpApiV2AuthorizerSerializer extends StdSerializer { - private static final long serialVersionUID = 42L; - - public HttpApiV2AuthorizerSerializer() { - super(HttpApiV2AuthorizerMap.class); - } - - @Override - public void serialize(HttpApiV2AuthorizerMap httpApiV2AuthorizerMap, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { - jsonGenerator.writeStartObject(); - if (httpApiV2AuthorizerMap.isJwt()) { - jsonGenerator.writeObjectField(JWT_KEY, httpApiV2AuthorizerMap.getJwtAuthorizer()); - } - if (httpApiV2AuthorizerMap.isLambda()) { - jsonGenerator.writeObjectField(LAMBDA_KEY, httpApiV2AuthorizerMap.getLambdaAuthorizerContext()); - } - jsonGenerator.writeEndObject(); - } - } -} \ No newline at end of file diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/HttpApiV2HttpContext.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/HttpApiV2HttpContext.java deleted file mode 100644 index e6eb12876..000000000 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/HttpApiV2HttpContext.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance - * with the License. A copy of the License is located at - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES - * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ -package com.amazonaws.serverless.proxy.model; - -public class HttpApiV2HttpContext { - private String method; - private String path; - private String protocol; - private String sourceIp; - private String userAgent; - - public String getMethod() { - return method; - } - - public void setMethod(String method) { - this.method = method; - } - - public String getPath() { - return path; - } - - public void setPath(String path) { - this.path = path; - } - - public String getProtocol() { - return protocol; - } - - public void setProtocol(String protocol) { - this.protocol = protocol; - } - - public String getSourceIp() { - return sourceIp; - } - - public void setSourceIp(String sourceIp) { - this.sourceIp = sourceIp; - } - - public String getUserAgent() { - return userAgent; - } - - public void setUserAgent(String userAgent) { - this.userAgent = userAgent; - } -} diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/HttpApiV2JwtAuthorizer.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/HttpApiV2JwtAuthorizer.java deleted file mode 100644 index d81a3c77f..000000000 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/HttpApiV2JwtAuthorizer.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance - * with the License. A copy of the License is located at - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES - * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ -package com.amazonaws.serverless.proxy.model; - -import java.util.List; -import java.util.Map; - -public class HttpApiV2JwtAuthorizer { - private Map claims; - private List scopes; - - public Map getClaims() { - return claims; - } - - public void setClaims(Map claims) { - this.claims = claims; - } - - public List getScopes() { - return scopes; - } - - public void setScopes(List scopes) { - this.scopes = scopes; - } -} diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/HttpApiV2ProxyRequest.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/HttpApiV2ProxyRequest.java deleted file mode 100644 index 023236cc7..000000000 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/HttpApiV2ProxyRequest.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance - * with the License. A copy of the License is located at - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES - * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ -package com.amazonaws.serverless.proxy.model; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonIgnore; - -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; - -public class HttpApiV2ProxyRequest { - private String version; - private String routeKey; - private String rawPath; - private String rawQueryString; - private List cookies; - private Map headers; - private Map queryStringParameters; - private String body; - private Map pathParameters; - private boolean isBase64Encoded; - private Map stageVariables; - private HttpApiV2ProxyRequestContext requestContext; - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public String getRouteKey() { - return routeKey; - } - - public void setRouteKey(String routeKey) { - this.routeKey = routeKey; - } - - public String getRawPath() { - return rawPath; - } - - public void setRawPath(String rawPath) { - this.rawPath = rawPath; - } - - public String getRawQueryString() { - return rawQueryString; - } - - public void setRawQueryString(String rawQueryString) { - this.rawQueryString = rawQueryString; - } - - public List getCookies() { - return cookies; - } - - public void setCookies(List cookies) { - this.cookies = cookies; - } - - public Map getHeaders() { - return headers; - } - - public void setHeaders(Map headers) { - this.headers = headers; - } - - public Map getQueryStringParameters() { - return queryStringParameters; - } - - public void setQueryStringParameters(Map queryStringParameters) { - this.queryStringParameters = queryStringParameters; - } - - public String getBody() { - return body; - } - - public Map getPathParameters() { - return pathParameters; - } - - public void setPathParameters(Map pathParameters) { - this.pathParameters = pathParameters; - } - - public void setBody(String body) { - this.body = body; - } - - @JsonProperty("isBase64Encoded") - public boolean isBase64Encoded() { - return isBase64Encoded; - } - - public void setBase64Encoded(boolean base64Encoded) { - isBase64Encoded = base64Encoded; - } - - public Map getStageVariables() { - return stageVariables; - } - - public void setStageVariables(Map stageVariables) { - this.stageVariables = stageVariables; - } - - public HttpApiV2ProxyRequestContext getRequestContext() { - return requestContext; - } - - public void setRequestContext(HttpApiV2ProxyRequestContext requestContext) { - this.requestContext = requestContext; - } - - @JsonIgnore - public RequestSource getRequestSource() { - return Optional.ofNullable(getRequestContext()) - .map(HttpApiV2ProxyRequestContext::getElb) - .map(albContext -> RequestSource.ALB) - .orElse(RequestSource.API_GATEWAY); - } -} diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/HttpApiV2ProxyRequestContext.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/HttpApiV2ProxyRequestContext.java deleted file mode 100644 index e5a5b9d2d..000000000 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/HttpApiV2ProxyRequestContext.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance - * with the License. A copy of the License is located at - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES - * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ -package com.amazonaws.serverless.proxy.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -@JsonIgnoreProperties(ignoreUnknown = true) -public class HttpApiV2ProxyRequestContext { - private String accountId; - private String apiId; - private String domainName; - private String domainPrefix; - private String requestId; - private String routeKey; - private String stage; - private String time; - private long timeEpoch; - private AlbContext elb; - - private HttpApiV2HttpContext http; - private HttpApiV2AuthorizerMap authorizer; - - public String getAccountId() { - return accountId; - } - - public void setAccountId(String accountId) { - this.accountId = accountId; - } - - public String getApiId() { - return apiId; - } - - public void setApiId(String apiId) { - this.apiId = apiId; - } - - public String getDomainName() { - return domainName; - } - - public void setDomainName(String domainName) { - this.domainName = domainName; - } - - public String getDomainPrefix() { - return domainPrefix; - } - - public void setDomainPrefix(String domainPrefix) { - this.domainPrefix = domainPrefix; - } - - public String getRequestId() { - return requestId; - } - - public void setRequestId(String requestId) { - this.requestId = requestId; - } - - public String getRouteKey() { - return routeKey; - } - - public void setRouteKey(String routeKey) { - this.routeKey = routeKey; - } - - public String getStage() { - return stage; - } - - public void setStage(String stage) { - this.stage = stage; - } - - public String getTime() { - return time; - } - - public void setTime(String time) { - this.time = time; - } - - public long getTimeEpoch() { - return timeEpoch; - } - - public void setTimeEpoch(long timeEpoch) { - this.timeEpoch = timeEpoch; - } - - public HttpApiV2HttpContext getHttp() { - return http; - } - - public void setHttp(HttpApiV2HttpContext http) { - this.http = http; - } - - public HttpApiV2AuthorizerMap getAuthorizer() { - return authorizer; - } - - public void setAuthorizer(HttpApiV2AuthorizerMap authorizer) { - this.authorizer = authorizer; - } - - public AlbContext getElb() { - return this.elb; - } - - public void setElb(AlbContext context) { - this.elb = context; - } - - -} diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/jaxrs/HttpApiV2SecurityContextTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/jaxrs/HttpApiV2SecurityContextTest.java index a4c6aa311..829ac6b51 100644 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/jaxrs/HttpApiV2SecurityContextTest.java +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/jaxrs/HttpApiV2SecurityContextTest.java @@ -2,7 +2,7 @@ import com.amazonaws.serverless.proxy.AwsHttpApiV2SecurityContextWriter; import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import org.junit.jupiter.api.Test; import jakarta.ws.rs.core.HttpHeaders; @@ -13,10 +13,10 @@ public class HttpApiV2SecurityContextTest { private static final String JWT_SUB_VALUE = "1234567890"; - HttpApiV2ProxyRequest EMPTY_AUTH = new AwsProxyRequestBuilder("/", "GET").toHttpApiV2Request(); - HttpApiV2ProxyRequest BASIC_AUTH = new AwsProxyRequestBuilder("/", "GET") + APIGatewayV2HTTPEvent EMPTY_AUTH = new AwsProxyRequestBuilder("/", "GET").toHttpApiV2Request(); + APIGatewayV2HTTPEvent BASIC_AUTH = new AwsProxyRequestBuilder("/", "GET") .authorizerPrincipal("test").toHttpApiV2Request(); - HttpApiV2ProxyRequest JWT_AUTH = new AwsProxyRequestBuilder("/", "GET") + APIGatewayV2HTTPEvent JWT_AUTH = new AwsProxyRequestBuilder("/", "GET") .authorizerPrincipal("test") .header(HttpHeaders.AUTHORIZATION, "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c") .toHttpApiV2Request(); diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpApiV2HttpServletRequestReaderTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpApiV2HttpServletRequestReaderTest.java index 12f694bc1..624c15ffe 100644 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpApiV2HttpServletRequestReaderTest.java +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpApiV2HttpServletRequestReaderTest.java @@ -3,8 +3,7 @@ import com.amazonaws.serverless.exceptions.InvalidRequestEventException; import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequestContext; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import org.junit.jupiter.api.Test; import jakarta.servlet.http.HttpServletRequest; @@ -17,12 +16,12 @@ public class AwsHttpApiV2HttpServletRequestReaderTest { @Test void reflection_getRequestClass_returnsCorrectType() { - assertSame(HttpApiV2ProxyRequest.class, reader.getRequestClass()); + assertSame(APIGatewayV2HTTPEvent.class, reader.getRequestClass()); } @Test void baseRequest_read_populatesSuccessfully() { - HttpApiV2ProxyRequest req = new AwsProxyRequestBuilder("/hello", "GET") + APIGatewayV2HTTPEvent req = new AwsProxyRequestBuilder("/hello", "GET") .referer("localhost") .userAgent("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.61 Safari/537.36") .queryString("param1", "value1") @@ -44,7 +43,7 @@ void baseRequest_read_populatesSuccessfully() { assertNotNull(servletRequest.getAttribute(AwsHttpApiV2HttpServletRequestReader.HTTP_API_CONTEXT_PROPERTY)); assertEquals("test", - ((HttpApiV2ProxyRequestContext)servletRequest.getAttribute(AwsHttpApiV2HttpServletRequestReader.HTTP_API_CONTEXT_PROPERTY)).getApiId()); + ((APIGatewayV2HTTPEvent.RequestContext)servletRequest.getAttribute(AwsHttpApiV2HttpServletRequestReader.HTTP_API_CONTEXT_PROPERTY)).getApiId()); } catch (InvalidRequestEventException e) { e.printStackTrace(); fail("Could not read request"); diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java index 9df66a891..ee48d6d1d 100644 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java @@ -15,6 +15,7 @@ import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; import com.amazonaws.serverless.proxy.model.*; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import com.fasterxml.jackson.core.JsonProcessingException; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.apache.commons.io.IOUtils; @@ -435,7 +436,7 @@ public InputStream buildStream() { } public InputStream toHttpApiV2RequestStream() { - HttpApiV2ProxyRequest req = toHttpApiV2Request(); + APIGatewayV2HTTPEvent req = toHttpApiV2Request(); try { String requestJson = LambdaContainerHandler.getObjectMapper().writeValueAsString(req); return new ByteArrayInputStream(requestJson.getBytes(StandardCharsets.UTF_8)); @@ -444,13 +445,13 @@ public InputStream toHttpApiV2RequestStream() { } } - public HttpApiV2ProxyRequest toHttpApiV2Request() { - HttpApiV2ProxyRequest req = new HttpApiV2ProxyRequest(); + public APIGatewayV2HTTPEvent toHttpApiV2Request() { + APIGatewayV2HTTPEvent req = new APIGatewayV2HTTPEvent(); req.setRawPath(request.getPath()); - req.setBase64Encoded(request.isBase64Encoded()); + req.setIsBase64Encoded(request.isBase64Encoded()); req.setBody(request.getBody()); if (request.getMultiValueHeaders() != null && request.getMultiValueHeaders().containsKey(HttpHeaders.COOKIE)) { - req.setCookies(Arrays.asList(request.getMultiValueHeaders().getFirst(HttpHeaders.COOKIE).split(";"))); + req.setCookies(Arrays.asList(request.getMultiValueHeaders().get(HttpHeaders.COOKIE).get(0).split(";"))); } req.setHeaders(new TreeMap<>(String.CASE_INSENSITIVE_ORDER)); if (request.getMultiValueHeaders() != null) { @@ -494,8 +495,8 @@ public HttpApiV2ProxyRequest toHttpApiV2Request() { req.setVersion("2.0"); req.setStageVariables(request.getStageVariables()); - HttpApiV2ProxyRequestContext ctx = new HttpApiV2ProxyRequestContext(); - HttpApiV2HttpContext httpCtx = new HttpApiV2HttpContext(); + APIGatewayV2HTTPEvent.RequestContext ctx = new APIGatewayV2HTTPEvent.RequestContext(); + APIGatewayV2HTTPEvent.RequestContext.Http httpCtx = new APIGatewayV2HTTPEvent.RequestContext.Http(); httpCtx.setMethod(request.getHttpMethod()); httpCtx.setPath(request.getPath()); httpCtx.setProtocol("HTTP/1.1"); @@ -520,12 +521,12 @@ public HttpApiV2ProxyRequest toHttpApiV2Request() { ctx.setTime(request.getRequestContext().getRequestTime()); if (request.getRequestContext().getAuthorizer() != null) { - HttpApiV2AuthorizerMap auth = new HttpApiV2AuthorizerMap(); - HttpApiV2JwtAuthorizer jwt = new HttpApiV2JwtAuthorizer(); + APIGatewayV2HTTPEvent.RequestContext.Authorizer auth = new APIGatewayV2HTTPEvent.RequestContext.Authorizer(); + APIGatewayV2HTTPEvent.RequestContext.Authorizer.JWT jwt = new APIGatewayV2HTTPEvent.RequestContext.Authorizer.JWT(); // TODO: Anything we should map here? jwt.setClaims(new HashMap<>()); jwt.setScopes(new ArrayList<>()); - auth.putJwtAuthorizer(jwt); + auth.setJwt(jwt); ctx.setAuthorizer(auth); } } diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/HttpApiV2ProxyRequestTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/HttpApiV2ProxyRequestTest.java deleted file mode 100644 index 3aa7cfdfc..000000000 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/HttpApiV2ProxyRequestTest.java +++ /dev/null @@ -1,210 +0,0 @@ -package com.amazonaws.serverless.proxy.model; - -import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; -import com.fasterxml.jackson.core.JsonProcessingException; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; - -import static org.junit.jupiter.api.Assertions.*; - -public class HttpApiV2ProxyRequestTest { - - private static final String BASE_PROXY_REQUEST = "{\n" + - " \"version\": \"2.0\",\n" + - " \"routeKey\": \"$default\",\n" + - " \"rawPath\": \"/my/path\",\n" + - " \"rawQueryString\": \"parameter1=value1¶meter1=value2¶meter2=value\",\n" + - " \"cookies\": [ \"cookie1\", \"cookie2\" ],\n" + - " \"headers\": {\n" + - " \"Header1\": \"value1\",\n" + - " \"Header2\": \"value2\"\n" + - " },\n" + - " \"queryStringParameters\": { \"parameter1\": \"value1,value2\", \"parameter2\": \"value\" },\n" + - " \"requestContext\": {\n" + - " \"accountId\": \"123456789012\",\n" + - " \"apiId\": \"api-id\",\n" + - " \"authorizer\": { \"jwt\": {\n" + - " \"claims\": {\"claim1\": \"value1\", \"claim2\": \"value2\"},\n" + - " \"scopes\": [\"scope1\", \"scope2\"]\n" + - " }\n" + - " },\n" + - " \"domainName\": \"id.execute-api.us-east-1.amazonaws.com\",\n" + - " \"domainPrefix\": \"id\",\n" + - " \"http\": {\n" + - " \"method\": \"POST\",\n" + - " \"path\": \"/my/path\",\n" + - " \"protocol\": \"HTTP/1.1\",\n" + - " \"sourceIp\": \"IP\",\n" + - " \"userAgent\": \"agent\"\n" + - " },\n" + - " \"requestId\": \"id\",\n" + - " \"routeKey\": \"$default\",\n" + - " \"stage\": \"$default\",\n" + - " \"time\": \"12/Mar/2020:19:03:58 +0000\",\n" + - " \"timeEpoch\": 1583348638390\n" + - " },\n" + - " \"body\": \"Hello from Lambda\",\n" + - " \"isBase64Encoded\": false,\n" + - " \"stageVariables\": {\"stageVariable1\": \"value1\", \"stageVariable2\": \"value2\"}\n" + - " }\n"; - private static final String NO_AUTH_PROXY = "{\n" + - " \"version\": \"2.0\",\n" + - " \"routeKey\": \"$default\",\n" + - " \"rawPath\": \"/my/path\",\n" + - " \"rawQueryString\": \"parameter1=value1¶meter1=value2¶meter2=value\",\n" + - " \"cookies\": [ \"cookie1\", \"cookie2\" ],\n" + - " \"headers\": {\n" + - " \"Header1\": \"value1\",\n" + - " \"Header2\": \"value2\"\n" + - " },\n" + - " \"queryStringParameters\": { \"parameter1\": \"value1,value2\", \"parameter2\": \"value\" },\n" + - " \"requestContext\": {\n" + - " \"accountId\": \"123456789012\",\n" + - " \"apiId\": \"api-id\",\n" + - " \"authorizer\": {\n " + - " },\n" + - " \"domainName\": \"id.execute-api.us-east-1.amazonaws.com\",\n" + - " \"domainPrefix\": \"id\",\n" + - " \"http\": {\n" + - " \"method\": \"POST\",\n" + - " \"path\": \"/my/path\",\n" + - " \"protocol\": \"HTTP/1.1\",\n" + - " \"sourceIp\": \"IP\",\n" + - " \"userAgent\": \"agent\"\n" + - " },\n" + - " \"requestId\": \"id\",\n" + - " \"routeKey\": \"$default\",\n" + - " \"stage\": \"$default\",\n" + - " \"time\": \"12/Mar/2020:19:03:58 +0000\",\n" + - " \"timeEpoch\": 1583348638390\n" + - " },\n" + - " \"body\": \"Hello from Lambda\",\n" + - " \"isBase64Encoded\": true,\n" + - " \"stageVariables\": {\"stageVariable1\": \"value1\", \"stageVariable2\": \"value2\"}\n" + - " }\n"; - private static final String LAMBDA_AUTHORIZER = "{\n" + - " \"version\": \"2.0\",\n" + - " \"routeKey\": \"$default\",\n" + - " \"rawPath\": \"/my/path\",\n" + - " \"rawQueryString\": \"parameter1=value1¶meter1=value2¶meter2=value\",\n" + - " \"cookies\": [ \"cookie1\", \"cookie2\" ],\n" + - " \"headers\": {\n" + - " \"Header1\": \"value1\",\n" + - " \"Header2\": \"value2\"\n" + - " },\n" + - " \"queryStringParameters\": { \"parameter1\": \"value1,value2\", \"parameter2\": \"value\" },\n" + - " \"requestContext\": {\n" + - " \"accountId\": \"123456789012\",\n" + - " \"apiId\": \"api-id\",\n" + - " \"authorizer\": { \"lambda\": {\n" + - " \"arrayKey\": [\n" + - " \"value1\",\n" + - " \"value2\"\n" + - " ],\n" + - " \"booleanKey\": true,\n" + - " \"mapKey\": {\n" + - " \"value1\": \"value2\"\n" + - " },\n" + - " \"numberKey\": 1,\n" + - " \"stringKey\": \"value\"\n" + - " }" + - " },\n" + - " \"domainName\": \"id.execute-api.us-east-1.amazonaws.com\",\n" + - " \"domainPrefix\": \"id\",\n" + - " \"http\": {\n" + - " \"method\": \"POST\",\n" + - " \"path\": \"/my/path\",\n" + - " \"protocol\": \"HTTP/1.1\",\n" + - " \"sourceIp\": \"IP\",\n" + - " \"userAgent\": \"agent\"\n" + - " },\n" + - " \"requestId\": \"id\",\n" + - " \"routeKey\": \"$default\",\n" + - " \"stage\": \"$default\",\n" + - " \"time\": \"12/Mar/2020:19:03:58 +0000\",\n" + - " \"timeEpoch\": 1583348638390\n" + - " },\n" + - " \"body\": \"Hello from Lambda\",\n" + - " \"isBase64Encoded\": false,\n" + - " \"stageVariables\": {\"stageVariable1\": \"value1\", \"stageVariable2\": \"value2\"}\n" + - " }\n"; - - @Test - void deserialize_fromJsonString_authorizerPopulatedCorrectly() { - try { - HttpApiV2ProxyRequest req = LambdaContainerHandler.getObjectMapper().readValue(BASE_PROXY_REQUEST, HttpApiV2ProxyRequest.class); - assertTrue(req.getRequestContext().getAuthorizer().getJwtAuthorizer().getClaims().containsKey("claim1")); - assertEquals(2, req.getRequestContext().getAuthorizer().getJwtAuthorizer().getScopes().size()); - assertEquals(RequestSource.API_GATEWAY, req.getRequestSource()); - } catch (JsonProcessingException e) { - e.printStackTrace(); - fail("Exception while parsing request" + e.getMessage()); - } - } - - @Test - void deserialize_fromJsonString_authorizerEmptyMap() { - try { - HttpApiV2ProxyRequest req = LambdaContainerHandler.getObjectMapper().readValue(NO_AUTH_PROXY, HttpApiV2ProxyRequest.class); - assertNotNull(req.getRequestContext().getAuthorizer()); - assertFalse(req.getRequestContext().getAuthorizer().isJwt()); - assertFalse(req.getRequestContext().getAuthorizer().isLambda()); - } catch (JsonProcessingException e) { - e.printStackTrace(); - fail("Exception while parsing request" + e.getMessage()); - } - } - - @Test - void deserialize_fromJsonString_lambdaAuthorizer() { - try { - HttpApiV2ProxyRequest req = LambdaContainerHandler.getObjectMapper().readValue(LAMBDA_AUTHORIZER, HttpApiV2ProxyRequest.class); - assertNotNull(req.getRequestContext().getAuthorizer()); - assertFalse(req.getRequestContext().getAuthorizer().isJwt()); - assertTrue(req.getRequestContext().getAuthorizer().isLambda()); - assertEquals(5, req.getRequestContext().getAuthorizer().getLambdaAuthorizerContext().size()); - assertEquals(1, req.getRequestContext().getAuthorizer().getLambdaAuthorizerContext().get("numberKey")); - } catch (JsonProcessingException e) { - e.printStackTrace(); - fail("Exception while parsing request" + e.getMessage()); - } - } - - @Test - void deserialize_fromJsonString_isBase64EncodedPopulates() { - try { - HttpApiV2ProxyRequest req = LambdaContainerHandler.getObjectMapper().readValue(BASE_PROXY_REQUEST, HttpApiV2ProxyRequest.class); - assertFalse(req.isBase64Encoded()); - req = LambdaContainerHandler.getObjectMapper().readValue(NO_AUTH_PROXY, HttpApiV2ProxyRequest.class); - assertTrue(req.isBase64Encoded()); - assertEquals(RequestSource.API_GATEWAY, req.getRequestSource()); - } catch (JsonProcessingException e) { - e.printStackTrace(); - fail("Exception while parsing request" + e.getMessage()); - } - } - - @Test - void serialize_toJsonString_authorizerPopulatesCorrectly() { - HttpApiV2ProxyRequest req = new HttpApiV2ProxyRequest(); - req.setBase64Encoded(false); - req.setRequestContext(new HttpApiV2ProxyRequestContext()); - req.getRequestContext().setAuthorizer(new HttpApiV2AuthorizerMap()); - req.getRequestContext().getAuthorizer().putJwtAuthorizer(new HttpApiV2JwtAuthorizer()); - ArrayList scopes = new ArrayList<>(); - scopes.add("first"); - scopes.add("second"); - req.getRequestContext().getAuthorizer().getJwtAuthorizer().setScopes(scopes); - - try { - String reqString = LambdaContainerHandler.getObjectMapper().writeValueAsString(req); - assertTrue(reqString.contains("\"scopes\":[\"first\",\"second\"]")); - assertTrue(reqString.contains("\"authorizer\":{\"jwt\":{")); - assertTrue(reqString.contains("\"isBase64Encoded\":false")); - } catch (JsonProcessingException e) { - e.printStackTrace(); - fail("Exception while serializing request" + e.getMessage()); - } - } -} diff --git a/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyLambdaContainerHandler.java b/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyLambdaContainerHandler.java index e290e284b..8b38886e8 100644 --- a/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyLambdaContainerHandler.java +++ b/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyLambdaContainerHandler.java @@ -22,9 +22,9 @@ import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import org.glassfish.jersey.internal.inject.AbstractBinder; import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.process.internal.RequestScoped; @@ -110,9 +110,9 @@ public static JerseyLambdaContainerHandler ge * ResourceConfig object * @return A JerseyLambdaContainerHandler object */ - public static JerseyLambdaContainerHandler getHttpApiV2ProxyHandler(Application jaxRsApplication) { - JerseyLambdaContainerHandler newHandler = new JerseyLambdaContainerHandler<>( - HttpApiV2ProxyRequest.class, + public static JerseyLambdaContainerHandler getHttpApiV2ProxyHandler(Application jaxRsApplication) { + JerseyLambdaContainerHandler newHandler = new JerseyLambdaContainerHandler<>( + APIGatewayV2HTTPEvent.class, AwsProxyResponse.class, new AwsHttpApiV2HttpServletRequestReader(), new AwsProxyHttpServletResponseWriter(true), diff --git a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyTest.java b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyTest.java index b845a0b69..7547467b2 100644 --- a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyTest.java +++ b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyTest.java @@ -22,8 +22,8 @@ import com.amazonaws.serverless.proxy.jersey.providers.ServletRequestFilter; import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.binary.Base64; @@ -81,7 +81,7 @@ public class JerseyAwsProxyTest { .property(LoggingFeature.LOGGING_FEATURE_VERBOSITY_SERVER, LoggingFeature.Verbosity.PAYLOAD_ANY); private static JerseyLambdaContainerHandler handler; - private static JerseyLambdaContainerHandler httpApiHandler; + private static JerseyLambdaContainerHandler httpApiHandler; private static JerseyLambdaContainerHandler handlerWithoutRegisteredDependencies = JerseyLambdaContainerHandler.getAwsProxyHandler(appWithoutRegisteredDependencies); diff --git a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyParamEncodingTest.java b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyParamEncodingTest.java index 9dc1ab32a..5fe808db2 100644 --- a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyParamEncodingTest.java +++ b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyParamEncodingTest.java @@ -8,9 +8,9 @@ import com.amazonaws.serverless.proxy.jersey.model.SingleValueModel; import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import com.fasterxml.jackson.databind.ObjectMapper; import org.glassfish.jersey.media.multipart.MultiPartFeature; import org.glassfish.jersey.server.ResourceConfig; @@ -73,7 +73,7 @@ public class JerseyParamEncodingTest { .register(new ResourceBinder()) .property("jersey.config.server.tracing.type", "ALL") .property("jersey.config.server.tracing.threshold", "VERBOSE"); - private static JerseyLambdaContainerHandler httpApiHandler; + private static JerseyLambdaContainerHandler httpApiHandler; private static Context lambdaContext = new MockLambdaContext(); diff --git a/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringLambdaContainerHandler.java b/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringLambdaContainerHandler.java index 5e56dba62..f09ffa598 100644 --- a/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringLambdaContainerHandler.java +++ b/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringLambdaContainerHandler.java @@ -18,8 +18,8 @@ import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; import com.amazonaws.serverless.proxy.internal.servlet.*; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import org.springframework.web.context.ConfigurableWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; @@ -80,8 +80,8 @@ public static SpringLambdaContainerHandler ge * @return An initialized instance of the `SpringLambdaContainerHandler` * @throws ContainerInitializationException When the Spring framework fails to start. */ - public static SpringLambdaContainerHandler getHttpApiV2ProxyHandler(Class... config) throws ContainerInitializationException { - return new SpringProxyHandlerBuilder() + public static SpringLambdaContainerHandler getHttpApiV2ProxyHandler(Class... config) throws ContainerInitializationException { + return new SpringProxyHandlerBuilder() .defaultHttpApiV2Proxy() .initializationWrapper(new InitializationWrapper()) .configurationClasses(config) diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringAwsProxyTest.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringAwsProxyTest.java index 1101efc8c..cbc269aba 100644 --- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringAwsProxyTest.java +++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringAwsProxyTest.java @@ -15,6 +15,7 @@ import com.amazonaws.serverless.proxy.spring.echoapp.model.MapResponseModel; import com.amazonaws.serverless.proxy.spring.echoapp.model.SingleValueModel; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.binary.Base64; @@ -49,7 +50,7 @@ public class SpringAwsProxyTest { private ObjectMapper objectMapper = new ObjectMapper(); private MockLambdaContext lambdaContext = new MockLambdaContext(); private static SpringLambdaContainerHandler handler; - private static SpringLambdaContainerHandler httpApiHandler; + private static SpringLambdaContainerHandler httpApiHandler; private AwsLambdaServletContainerHandler.StartupHandler h = (c -> { FilterRegistration.Dynamic registration = c.addFilter("UnauthenticatedFilter", UnauthenticatedFilter.class); diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/extensibility/CustomSpringLambdaContainerHandler.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/extensibility/CustomSpringLambdaContainerHandler.java index 4e5fe2783..9f1553777 100644 --- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/extensibility/CustomSpringLambdaContainerHandler.java +++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/extensibility/CustomSpringLambdaContainerHandler.java @@ -5,8 +5,8 @@ import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletResponse; import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest; import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import org.springframework.web.context.ConfigurableWebApplicationContext; import jakarta.servlet.ServletRegistration; @@ -51,8 +51,8 @@ public static SpringLambdaContainerHandler ge * @return An initialized instance of the `SpringLambdaContainerHandler` * @throws ContainerInitializationException When the Spring framework fails to start. */ - public static SpringLambdaContainerHandler getHttpApiV2ProxyHandler(Class... config) throws ContainerInitializationException { - return new CustomSpringProxyHandlerBuilder() + public static SpringLambdaContainerHandler getHttpApiV2ProxyHandler(Class... config) throws ContainerInitializationException { + return new CustomSpringProxyHandlerBuilder() .defaultHttpApiV2Proxy() .initializationWrapper(new InitializationWrapper()) .configurationClasses(config) diff --git a/aws-serverless-java-container-springboot3/pom.xml b/aws-serverless-java-container-springboot3/pom.xml index 974345fe7..7c0e6d83b 100644 --- a/aws-serverless-java-container-springboot3/pom.xml +++ b/aws-serverless-java-container-springboot3/pom.xml @@ -284,6 +284,14 @@ + + org.apache.maven.plugins + maven-compiler-plugin + + 10 + 10 + + diff --git a/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootLambdaContainerHandler.java b/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootLambdaContainerHandler.java index 1f7719e9a..bcd93d01b 100644 --- a/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootLambdaContainerHandler.java +++ b/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootLambdaContainerHandler.java @@ -18,10 +18,10 @@ import com.amazonaws.serverless.proxy.internal.testutils.Timer; import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest; import com.amazonaws.serverless.proxy.spring.embedded.ServerlessReactiveServletEmbeddedServerFactory; import com.amazonaws.serverless.proxy.spring.embedded.ServerlessServletEmbeddedServerFactory; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; @@ -30,7 +30,6 @@ import org.springframework.context.ConfigurableApplicationContext; import jakarta.servlet.Servlet; -import jakarta.servlet.ServletRegistration; import jakarta.servlet.http.HttpServletRequest; import java.util.concurrent.CountDownLatch; @@ -94,9 +93,9 @@ public static SpringBootLambdaContainerHandler getHttpApiV2ProxyHandler(Class springBootInitializer, String... profiles) + public static SpringBootLambdaContainerHandler getHttpApiV2ProxyHandler(Class springBootInitializer, String... profiles) throws ContainerInitializationException { - return new SpringBootProxyHandlerBuilder() + return new SpringBootProxyHandlerBuilder() .defaultHttpApiV2Proxy() .initializationWrapper(new InitializationWrapper()) .springBootApplication(springBootInitializer) diff --git a/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringDelegatingLambdaContainerHandler.java b/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringDelegatingLambdaContainerHandler.java index ce3142369..6e267b284 100644 --- a/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringDelegatingLambdaContainerHandler.java +++ b/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringDelegatingLambdaContainerHandler.java @@ -8,6 +8,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import org.springframework.cloud.function.serverless.web.FunctionClassUtils; import org.springframework.cloud.function.serverless.web.ProxyHttpServletRequest; import org.springframework.cloud.function.serverless.web.ProxyMvc; @@ -20,7 +21,6 @@ import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletResponse; import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletResponseWriter; import com.amazonaws.serverless.proxy.model.AwsProxyRequest; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; import com.fasterxml.jackson.databind.ObjectMapper; @@ -112,7 +112,7 @@ private HttpServletRequest generateRequest(Map request, Context lambdaContext, S @SuppressWarnings({ "rawtypes", "unchecked" }) public HttpServletRequest generateRequest2(Map request, Context lambdaContext, SecurityContextWriter securityWriter) { - HttpApiV2ProxyRequest v2Request = this.mapper.convertValue(request, HttpApiV2ProxyRequest.class); + APIGatewayV2HTTPEvent v2Request = this.mapper.convertValue(request, APIGatewayV2HTTPEvent.class); ProxyHttpServletRequest httpRequest = new ProxyHttpServletRequest(this.mvc.getApplicationContext().getServletContext(), v2Request.getRequestContext().getHttp().getMethod(), v2Request.getRequestContext().getHttp().getPath()); diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/servletapp/LambdaHandler.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/servletapp/LambdaHandler.java index 88441988e..60db19bad 100644 --- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/servletapp/LambdaHandler.java +++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/servletapp/LambdaHandler.java @@ -6,15 +6,15 @@ import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest; import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler; import com.amazonaws.serverless.proxy.spring.SpringBootProxyHandlerBuilder; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; public class LambdaHandler implements RequestHandler { private static SpringBootLambdaContainerHandler handler; - private static SpringBootLambdaContainerHandler httpApiHandler; + private static SpringBootLambdaContainerHandler httpApiHandler; private String type; public LambdaHandler(String reqType) { @@ -31,7 +31,7 @@ public LambdaHandler(String reqType) { .buildAndInitialize(); break; case "HTTP_API": - httpApiHandler = new SpringBootProxyHandlerBuilder() + httpApiHandler = new SpringBootProxyHandlerBuilder() .defaultHttpApiV2Proxy() .initializationWrapper(new InitializationWrapper()) .servletApplication() diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/servletapp/LambdaStreamHandler.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/servletapp/LambdaStreamHandler.java index fd7d71d79..d2a3c8d0c 100644 --- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/servletapp/LambdaStreamHandler.java +++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/servletapp/LambdaStreamHandler.java @@ -2,15 +2,13 @@ import com.amazonaws.serverless.exceptions.ContainerInitializationException; import com.amazonaws.serverless.proxy.InitializationWrapper; -import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest; import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler; import com.amazonaws.serverless.proxy.spring.SpringBootProxyHandlerBuilder; import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import java.io.IOException; import java.io.InputStream; @@ -18,7 +16,7 @@ public class LambdaStreamHandler implements RequestStreamHandler { private static SpringBootLambdaContainerHandler handler; - private static SpringBootLambdaContainerHandler httpApiHandler; + private static SpringBootLambdaContainerHandler httpApiHandler; private String type; public LambdaStreamHandler(String reqType) { @@ -35,7 +33,7 @@ public LambdaStreamHandler(String reqType) { .buildAndInitialize(); break; case "HTTP_API": - httpApiHandler = new SpringBootProxyHandlerBuilder() + httpApiHandler = new SpringBootProxyHandlerBuilder() .defaultHttpApiV2Proxy() .initializationWrapper(new InitializationWrapper()) .servletApplication() diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/webfluxapp/LambdaHandler.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/webfluxapp/LambdaHandler.java index 0eb52a7bc..7ea1d6e09 100644 --- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/webfluxapp/LambdaHandler.java +++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/webfluxapp/LambdaHandler.java @@ -5,15 +5,15 @@ import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest; import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler; import com.amazonaws.serverless.proxy.spring.SpringBootProxyHandlerBuilder; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; public class LambdaHandler implements RequestHandler { private static SpringBootLambdaContainerHandler handler; - private static SpringBootLambdaContainerHandler httpApiHandler; + private static SpringBootLambdaContainerHandler httpApiHandler; private String type; @@ -30,7 +30,7 @@ public LambdaHandler(String reqType) { .buildAndInitialize(); break; case "HTTP_API": - httpApiHandler = new SpringBootProxyHandlerBuilder() + httpApiHandler = new SpringBootProxyHandlerBuilder() .defaultHttpApiV2Proxy() .initializationWrapper(new InitializationWrapper()) .springBootApplication(WebFluxTestApplication.class) From 608621650bb03c3cadad76bc7bf5a942a7799523 Mon Sep 17 00:00:00 2001 From: mbfreder Date: Mon, 9 Oct 2023 01:59:08 -0700 Subject: [PATCH 3/8] Migrate to APIGatewayV2HTTPEvent in struts and spark --- .../serverless/proxy/spark/SparkLambdaContainerHandler.java | 6 +++--- .../serverless/proxy/spark/HelloWorldSparkStreamTest.java | 3 +-- .../proxy/struts/StrutsLambdaContainerHandler.java | 4 ++-- .../serverless/proxy/struts/StrutsAwsProxyTest.java | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/aws-serverless-java-container-spark/src/main/java/com/amazonaws/serverless/proxy/spark/SparkLambdaContainerHandler.java b/aws-serverless-java-container-spark/src/main/java/com/amazonaws/serverless/proxy/spark/SparkLambdaContainerHandler.java index 9c1b47511..8db251aef 100644 --- a/aws-serverless-java-container-spark/src/main/java/com/amazonaws/serverless/proxy/spark/SparkLambdaContainerHandler.java +++ b/aws-serverless-java-container-spark/src/main/java/com/amazonaws/serverless/proxy/spark/SparkLambdaContainerHandler.java @@ -119,7 +119,7 @@ public static SparkLambdaContainerHandler get } /** - * Returns a new instance of an SparkLambdaContainerHandler initialized to work with HttpApiV2ProxyRequest + * Returns a new instance of an SparkLambdaContainerHandler initialized to work with APIGatewayV2HTTPEvent * and AwsProxyResponse objects. * * @return a new instance of SparkLambdaContainerHandler @@ -127,9 +127,9 @@ public static SparkLambdaContainerHandler get * @throws ContainerInitializationException Throws this exception if we fail to initialize the Spark container. * This could be caused by the introspection used to insert the library as the default embedded container */ - public static SparkLambdaContainerHandler getHttpApiV2ProxyHandler() + public static SparkLambdaContainerHandler getHttpApiV2ProxyHandler() throws ContainerInitializationException { - SparkLambdaContainerHandler newHandler = new SparkLambdaContainerHandler<>(HttpApiV2ProxyRequest.class, + SparkLambdaContainerHandler newHandler = new SparkLambdaContainerHandler<>(APIGatewayV2HTTPEvent.class, AwsProxyResponse.class, new AwsHttpApiV2HttpServletRequestReader(), new AwsProxyHttpServletResponseWriter(true), diff --git a/aws-serverless-java-container-spark/src/test/java/com/amazonaws/serverless/proxy/spark/HelloWorldSparkStreamTest.java b/aws-serverless-java-container-spark/src/test/java/com/amazonaws/serverless/proxy/spark/HelloWorldSparkStreamTest.java index 33bc19f1d..bece71fac 100644 --- a/aws-serverless-java-container-spark/src/test/java/com/amazonaws/serverless/proxy/spark/HelloWorldSparkStreamTest.java +++ b/aws-serverless-java-container-spark/src/test/java/com/amazonaws/serverless/proxy/spark/HelloWorldSparkStreamTest.java @@ -8,7 +8,6 @@ import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; -import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest; import com.amazonaws.services.lambda.runtime.Context; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.params.ParameterizedTest; @@ -39,7 +38,7 @@ public class HelloWorldSparkStreamTest { private static final String COOKIE_PATH = "/"; private static SparkLambdaContainerHandler handler; - private static SparkLambdaContainerHandler httpApiHandler; + private static SparkLambdaContainerHandler httpApiHandler; private String type; diff --git a/aws-serverless-java-container-struts/src/main/java/com/amazonaws/serverless/proxy/struts/StrutsLambdaContainerHandler.java b/aws-serverless-java-container-struts/src/main/java/com/amazonaws/serverless/proxy/struts/StrutsLambdaContainerHandler.java index 257de488c..02b7b4260 100644 --- a/aws-serverless-java-container-struts/src/main/java/com/amazonaws/serverless/proxy/struts/StrutsLambdaContainerHandler.java +++ b/aws-serverless-java-container-struts/src/main/java/com/amazonaws/serverless/proxy/struts/StrutsLambdaContainerHandler.java @@ -71,9 +71,9 @@ public static StrutsLambdaContainerHandler ge new AwsProxyExceptionHandler()); } - public static StrutsLambdaContainerHandler getHttpApiV2ProxyHandler() { + public static StrutsLambdaContainerHandler getHttpApiV2ProxyHandler() { return new StrutsLambdaContainerHandler( - HttpApiV2ProxyRequest.class, + APIGatewayV2HTTPEvent.class, AwsProxyResponse.class, new AwsHttpApiV2HttpServletRequestReader(), new AwsProxyHttpServletResponseWriter(true), diff --git a/aws-serverless-java-container-struts/src/test/java/com/amazonaws/serverless/proxy/struts/StrutsAwsProxyTest.java b/aws-serverless-java-container-struts/src/test/java/com/amazonaws/serverless/proxy/struts/StrutsAwsProxyTest.java index 6d7e2a37f..4ae816b8a 100644 --- a/aws-serverless-java-container-struts/src/test/java/com/amazonaws/serverless/proxy/struts/StrutsAwsProxyTest.java +++ b/aws-serverless-java-container-struts/src/test/java/com/amazonaws/serverless/proxy/struts/StrutsAwsProxyTest.java @@ -62,7 +62,7 @@ public class StrutsAwsProxyTest extends StrutsRestTestCase { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private final StrutsLambdaContainerHandler handler = StrutsLambdaContainerHandler .getAwsProxyHandler(); - private final StrutsLambdaContainerHandler httpApiHandler = StrutsLambdaContainerHandler + private final StrutsLambdaContainerHandler httpApiHandler = StrutsLambdaContainerHandler .getHttpApiV2ProxyHandler(); private final Context lambdaContext = new MockLambdaContext(); private String type; From abc09a8c28d6000815a138f7e0e21039402ca5b4 Mon Sep 17 00:00:00 2001 From: mbfreder Date: Tue, 10 Oct 2023 10:26:47 -0700 Subject: [PATCH 4/8] migrated to APIGatewayProxyRequestEvent and ALB --- .../proxy/AwsAlbExceptionHandler.java | 90 ++++ .../proxy/AwsProxySecurityContextWriter.java | 6 +- .../serverless/proxy/RequestReader.java | 5 + .../proxy/SecurityContextWriter.java | 1 - .../jaxrs/AwsProxySecurityContext.java | 62 +-- .../ApacheCombinedServletLogFormatter.java | 6 +- .../servlet/AwsAlbHttpServletRequest.java | 453 ++++++++++++++++++ .../AwsAlbHttpServletRequestReader.java | 78 +++ .../AwsAlbHttpServletResponseWriter.java | 83 ++++ .../servlet/AwsAlbSecurityContext.java | 89 ++++ .../servlet/AwsAlbSecurityContextWriter.java | 25 + .../servlet/AwsHttpServletRequest.java | 53 +- .../servlet/AwsHttpServletRequestHelper.java | 449 +++++++++++++++++ .../servlet/AwsHttpServletResponse.java | 16 +- .../servlet/AwsProxyHttpServletRequest.java | 261 ++++------ .../AwsProxyHttpServletRequestReader.java | 23 +- .../AwsProxyHttpServletResponseWriter.java | 11 - .../servlet/AwsProxyRequestDispatcher.java | 6 +- .../ServletLambdaContainerHandlerBuilder.java | 16 +- .../serverless/proxy/model/AlbContext.java | 30 -- .../model/ApiGatewayAuthorizerContext.java | 80 ---- .../model/ApiGatewayRequestIdentity.java | 182 ------- .../proxy/model/AwsProxyRequest.java | 203 -------- .../proxy/model/AwsProxyRequestContext.java | 205 -------- .../proxy/model/AwsProxyResponse.java | 12 +- .../proxy/model/CognitoAuthorizerClaims.java | 183 ------- .../serverless/proxy/model/RequestSource.java | 18 - .../proxy/model/SingleValueHeaders.java | 24 - .../proxy/AwsAlbExceptionHandlerTest.java | 259 ++++++++++ .../proxy/AwsProxyExceptionHandlerTest.java | 13 +- .../AwsProxySecurityContextWriterTest.java | 6 +- .../internal/LambdaContainerHandlerTest.java | 8 +- .../jaxrs/AwsProxySecurityContextTest.java | 36 +- ...ApacheCombinedServletLogFormatterTest.java | 16 +- .../AwsAlbHttpServletRequestReaderTest.java | 161 +++++++ .../AwsAlbHttpServletResponseWriterTest.java | 44 ++ .../servlet/AwsAlbSecurityContextTest.java | 65 +++ .../AwsAlbSecurityContextWriterTest.java | 39 ++ .../internal/servlet/AwsAsyncContextTest.java | 6 +- .../servlet/AwsHttpServletRequestTest.java | 49 +- .../AwsProxyHttpServletRequestFormTest.java | 12 +- .../AwsProxyHttpServletRequestReaderTest.java | 23 +- .../AwsProxyHttpServletRequestTest.java | 6 +- .../AwsProxyRequestDispatcherTest.java | 24 +- ...vletLambdaContainerHandlerBuilderTest.java | 10 +- .../testutils/AwsProxyRequestBuilder.java | 190 ++++---- .../ApiGatewayAuthorizerContextTest.java | 15 +- .../proxy/model/AwsProxyRequestTest.java | 47 +- .../model/CognitoAuthorizerClaimsTest.java | 110 ----- .../jersey/JerseyLambdaContainerHandler.java | 28 +- .../proxy/jersey/EchoJerseyResource.java | 14 +- .../proxy/jersey/JerseyAwsProxyTest.java | 40 +- .../proxy/jersey/JerseyInjectionTest.java | 4 +- .../proxy/jersey/JerseyParamEncodingTest.java | 28 +- .../spring/SpringLambdaContainerHandler.java | 23 +- .../serverless/proxy/spring/SlowAppTest.java | 5 +- .../proxy/spring/SpringAwsProxyTest.java | 38 +- .../spring/SpringServletContextTest.java | 28 +- .../proxy/spring/StaticAppProxyTest.java | 7 +- .../proxy/spring/echoapp/EchoResource.java | 9 +- .../CustomSpringLambdaContainerHandler.java | 10 +- .../extensibility/StreamLambdaHandler.java | 4 +- .../spring/profile/SpringProfileTest.java | 10 +- .../spring/springslowapp/LambdaHandler.java | 10 +- .../proxy/spring/staticapp/LambdaHandler.java | 8 +- .../SpringBootLambdaContainerHandler.java | 6 +- ...pringDelegatingLambdaContainerHandler.java | 23 +- .../proxy/spring/SecurityAppTest.java | 4 +- .../proxy/spring/ServletAppTest.java | 4 +- .../serverless/proxy/spring/SlowAppTest.java | 4 +- ...DelegatingLambdaContainerHandlerTests.java | 2 +- .../proxy/spring/WebFluxAppTest.java | 2 - ...rlessServletEmbeddedServerFactoryTest.java | 6 +- .../spring/securityapp/LambdaHandler.java | 8 +- .../spring/servletapp/LambdaHandler.java | 20 +- .../servletapp/LambdaStreamHandler.java | 31 +- .../proxy/spring/slowapp/LambdaHandler.java | 11 +- .../spring/webfluxapp/LambdaHandler.java | 18 +- .../src/main/java/StreamLambdaHandler.java | 4 +- .../test/java/StreamLambdaHandlerTest.java | 3 +- .../src/main/java/StreamLambdaHandler.java | 4 +- .../test/java/StreamLambdaHandlerTest.java | 3 +- .../src/main/java/StreamLambdaHandler.java | 4 +- .../test/java/StreamLambdaHandlerTest.java | 3 +- pom.xml | 16 +- 85 files changed, 2533 insertions(+), 1718 deletions(-) create mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/AwsAlbExceptionHandler.java create mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletRequest.java create mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletRequestReader.java create mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletResponseWriter.java create mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbSecurityContext.java create mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbSecurityContextWriter.java create mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletRequestHelper.java delete mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AlbContext.java delete mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/ApiGatewayAuthorizerContext.java delete mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/ApiGatewayRequestIdentity.java delete mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AwsProxyRequest.java delete mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AwsProxyRequestContext.java delete mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/CognitoAuthorizerClaims.java delete mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/RequestSource.java delete mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/SingleValueHeaders.java create mode 100644 aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsAlbExceptionHandlerTest.java create mode 100644 aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletRequestReaderTest.java create mode 100644 aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletResponseWriterTest.java create mode 100644 aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbSecurityContextTest.java create mode 100644 aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbSecurityContextWriterTest.java delete mode 100644 aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/CognitoAuthorizerClaimsTest.java diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/AwsAlbExceptionHandler.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/AwsAlbExceptionHandler.java new file mode 100644 index 000000000..9420e0d32 --- /dev/null +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/AwsAlbExceptionHandler.java @@ -0,0 +1,90 @@ +package com.amazonaws.serverless.proxy; + +import com.amazonaws.serverless.exceptions.InvalidRequestEventException; +import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; +import com.amazonaws.serverless.proxy.model.AwsProxyResponse; +import com.amazonaws.serverless.proxy.model.ErrorModel; +import com.fasterxml.jackson.core.JsonProcessingException; +import jakarta.ws.rs.InternalServerErrorException; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class AwsAlbExceptionHandler implements ExceptionHandler{ + + private Logger log = LoggerFactory.getLogger(AwsAlbExceptionHandler.class); + + //------------------------------------------------------------- + // Constants + //------------------------------------------------------------- + + static final String INTERNAL_SERVER_ERROR = "Internal Server Error"; + static final String GATEWAY_TIMEOUT_ERROR = "Gateway timeout"; + + + //------------------------------------------------------------- + // Variables - Private - Static + //------------------------------------------------------------- + + private static final Map> headers = new HashMap<>(); + + //------------------------------------------------------------- + // Constructors + //------------------------------------------------------------- + + static { + List values = new ArrayList<>(); + values.add(MediaType.APPLICATION_JSON); + headers.put(HttpHeaders.CONTENT_TYPE, values); + } + @Override + public AwsProxyResponse handle(Throwable ex) { + log.error("Called exception handler for:", ex); + + // adding a print stack trace in case we have no appender or we are running inside SAM local, where need the + // output to go to the stderr. + ex.printStackTrace(); + AwsProxyResponse responseEvent = new AwsProxyResponse(); + + responseEvent.setMultiValueHeaders(headers); + if (ex instanceof InvalidRequestEventException || ex instanceof InternalServerErrorException) { + //return new APIGatewayProxyResponseEvent(500, headers, getErrorJson(INTERNAL_SERVER_ERROR)); + responseEvent.setBody(getErrorJson(INTERNAL_SERVER_ERROR)); + responseEvent.setStatusCode(500); + return responseEvent; + } else { + responseEvent.setBody(getErrorJson(GATEWAY_TIMEOUT_ERROR)); + responseEvent.setStatusCode(502); + return responseEvent; + } + } + + @Override + public void handle(Throwable ex, OutputStream stream) throws IOException { + AwsProxyResponse response = handle(ex); + + LambdaContainerHandler.getObjectMapper().writeValue(stream, response); + } + + //------------------------------------------------------------- + // Methods - Protected + //------------------------------------------------------------- + + String getErrorJson(String message) { + + try { + return LambdaContainerHandler.getObjectMapper().writeValueAsString(new ErrorModel(message)); + } catch (JsonProcessingException e) { + log.error("Could not produce error JSON", e); + return "{ \"message\": \"" + message + "\" }"; + } + } +} diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/AwsProxySecurityContextWriter.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/AwsProxySecurityContextWriter.java index 8a58bc478..0ee3e42fc 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/AwsProxySecurityContextWriter.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/AwsProxySecurityContextWriter.java @@ -13,16 +13,16 @@ package com.amazonaws.serverless.proxy; import com.amazonaws.serverless.proxy.internal.jaxrs.AwsProxySecurityContext; -import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import jakarta.ws.rs.core.SecurityContext; /** * Default implementation of SecurityContextWriter. Creates a SecurityContext object based on an API Gateway * event and the Lambda context. This returns the default AwsProxySecurityContext instance. */ -public class AwsProxySecurityContextWriter implements SecurityContextWriter { +public class AwsProxySecurityContextWriter implements SecurityContextWriter { //------------------------------------------------------------- // Variables - Private - Static @@ -36,7 +36,7 @@ public class AwsProxySecurityContextWriter implements SecurityContextWriter { */ public static final String ALB_CONTEXT_PROPERTY = "com.amazonaws.alb.request.context"; + /** + * The key to store the entire Application Load Balancer event + */ + public static final String ALB_EVENT_PROPERTY = "com.amazonaws.alb.request"; + /** * The key to store the entire API Gateway event */ diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/SecurityContextWriter.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/SecurityContextWriter.java index 27a4c6c75..5da54b678 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/SecurityContextWriter.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/SecurityContextWriter.java @@ -12,7 +12,6 @@ */ package com.amazonaws.serverless.proxy; -import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.services.lambda.runtime.Context; import jakarta.ws.rs.core.SecurityContext; diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/jaxrs/AwsProxySecurityContext.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/jaxrs/AwsProxySecurityContext.java index 7c128cabe..38328a319 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/jaxrs/AwsProxySecurityContext.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/jaxrs/AwsProxySecurityContext.java @@ -12,13 +12,13 @@ */ package com.amazonaws.serverless.proxy.internal.jaxrs; -import com.amazonaws.serverless.proxy.model.AwsProxyRequest; -import com.amazonaws.serverless.proxy.model.CognitoAuthorizerClaims; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import jakarta.ws.rs.core.SecurityContext; import java.security.Principal; +import java.util.Map; /** * default implementation of the SecurityContext object. This class supports 3 API Gateway's authorization methods: @@ -35,12 +35,14 @@ public class AwsProxySecurityContext // Constants - Package //------------------------------------------------------------- - static final String AUTH_SCHEME_CUSTOM = "CUSTOM_AUTHORIZER"; + public static final String AUTH_SCHEME_CUSTOM = "CUSTOM_AUTHORIZER"; static final String AUTH_SCHEME_COGNITO_POOL = "COGNITO_USER_POOL"; static final String AUTH_SCHEME_AWS_IAM = "AWS_IAM"; - - static final String ALB_ACESS_TOKEN_HEADER = "x-amzn-oidc-accesstoken"; - static final String ALB_IDENTITY_HEADER = "x-amzn-oidc-identity"; + static final String PRINCIPAL_ID_KEY = "principal_id"; + static final String CLAIMS_KEY = "claims"; + static final String SUB_KEY = "sub"; + public static final String ALB_ACESS_TOKEN_HEADER = "x-amzn-oidc-accesstoken"; + public static final String ALB_IDENTITY_HEADER = "x-amzn-oidc-identity"; //------------------------------------------------------------- @@ -48,7 +50,7 @@ public class AwsProxySecurityContext //------------------------------------------------------------- private Context lambdaContext; - private AwsProxyRequest event; + private APIGatewayProxyRequestEvent event; public Context getLambdaContext() { @@ -56,7 +58,7 @@ public Context getLambdaContext() { } - public AwsProxyRequest getEvent() { + public APIGatewayProxyRequestEvent getEvent() { return event; } @@ -64,7 +66,7 @@ public AwsProxyRequest getEvent() { // Constructors //------------------------------------------------------------- - public AwsProxySecurityContext(final Context lambdaContext, final AwsProxyRequest event) { + public AwsProxySecurityContext(final Context lambdaContext, final APIGatewayProxyRequestEvent event) { this.lambdaContext = lambdaContext; this.event = event; } @@ -83,12 +85,7 @@ public Principal getUserPrincipal() { if (getAuthenticationScheme().equals(AUTH_SCHEME_CUSTOM) || getAuthenticationScheme().equals(AUTH_SCHEME_AWS_IAM)) { return () -> { if (getAuthenticationScheme().equals(AUTH_SCHEME_CUSTOM)) { - switch (event.getRequestSource()) { - case API_GATEWAY: - return event.getRequestContext().getAuthorizer().getPrincipalId(); - case ALB: - return event.getMultiValueHeaders().getFirst(ALB_IDENTITY_HEADER); - } + return event.getRequestContext().getAuthorizer().get(PRINCIPAL_ID_KEY).toString(); } else if (getAuthenticationScheme().equals(AUTH_SCHEME_AWS_IAM)) { // if we received credentials from Cognito Federated Identities then we return the identity id if (event.getRequestContext().getIdentity().getCognitoIdentityId() != null) { @@ -104,7 +101,7 @@ public Principal getUserPrincipal() { } if (getAuthenticationScheme().equals(AUTH_SCHEME_COGNITO_POOL)) { - return new CognitoUserPoolPrincipal(event.getRequestContext().getAuthorizer().getClaims()); + return new CognitoUserPoolPrincipal((Map) event.getRequestContext().getAuthorizer().get(CLAIMS_KEY)); } throw new RuntimeException("Cannot recognize authorization scheme in event"); @@ -125,24 +122,15 @@ public boolean isSecure() { @Override public String getAuthenticationScheme() { - switch (event.getRequestSource()) { - case API_GATEWAY: - if (event.getRequestContext().getAuthorizer() != null && event.getRequestContext().getAuthorizer().getClaims() != null - && event.getRequestContext().getAuthorizer().getClaims().getSubject() != null) { - return AUTH_SCHEME_COGNITO_POOL; - } else if (event.getRequestContext().getAuthorizer() != null) { - return AUTH_SCHEME_CUSTOM; - } else if (event.getRequestContext().getIdentity().getAccessKey() != null) { - return AUTH_SCHEME_AWS_IAM; - } else { - return null; - } - case ALB: - if (event.getMultiValueHeaders().containsKey(ALB_ACESS_TOKEN_HEADER)) { - return AUTH_SCHEME_CUSTOM; - } + if (event.getRequestContext().getAuthorizer() != null && ((Map) event.getRequestContext().getAuthorizer().get(CLAIMS_KEY)).get(SUB_KEY) != null) { + return AUTH_SCHEME_COGNITO_POOL; + } else if (event.getRequestContext().getAuthorizer() != null) { + return AUTH_SCHEME_CUSTOM; + } else if (event.getRequestContext().getIdentity() != null && event.getRequestContext().getIdentity().getAccessKey() != null) { + return AUTH_SCHEME_AWS_IAM; + } else { + return null; } - return null; } @@ -152,18 +140,18 @@ public String getAuthenticationScheme() { */ public static class CognitoUserPoolPrincipal implements Principal { - private CognitoAuthorizerClaims claims; + private Map claims; - CognitoUserPoolPrincipal(CognitoAuthorizerClaims c) { + CognitoUserPoolPrincipal(Map c) { claims = c; } @Override public String getName() { - return claims.getSubject(); + return claims.get(SUB_KEY); } - public CognitoAuthorizerClaims getClaims() { + public Map getClaims() { return claims; } } diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ApacheCombinedServletLogFormatter.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ApacheCombinedServletLogFormatter.java index b460ae133..fffb7e418 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ApacheCombinedServletLogFormatter.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ApacheCombinedServletLogFormatter.java @@ -13,8 +13,8 @@ package com.amazonaws.serverless.proxy.internal.servlet; import com.amazonaws.serverless.proxy.LogFormatter; -import com.amazonaws.serverless.proxy.model.AwsProxyRequestContext; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -78,7 +78,7 @@ public ApacheCombinedServletLogFormatter() { public String format(ContainerRequestType servletRequest, ContainerResponseType servletResponse, SecurityContext ctx) { //LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" combined StringBuilder logLineBuilder = new StringBuilder(); - AwsProxyRequestContext gatewayContext = (AwsProxyRequestContext)servletRequest.getAttribute(API_GATEWAY_CONTEXT_PROPERTY); + APIGatewayProxyRequestEvent.ProxyRequestContext gatewayContext = (APIGatewayProxyRequestEvent.ProxyRequestContext)servletRequest.getAttribute(API_GATEWAY_CONTEXT_PROPERTY); APIGatewayV2HTTPEvent.RequestContext httpApiContext = (APIGatewayV2HTTPEvent.RequestContext)servletRequest.getAttribute(HTTP_API_CONTEXT_PROPERTY); // %h @@ -107,7 +107,7 @@ public String format(ContainerRequestType servletRequest, ContainerResponseType // %t long timeEpoch = ZonedDateTime.now(clock).toEpochSecond(); - if (gatewayContext != null && gatewayContext.getRequestTimeEpoch() > 0) { + if (gatewayContext != null && gatewayContext.getRequestTimeEpoch() != null && gatewayContext.getRequestTimeEpoch() > 0) { timeEpoch = gatewayContext.getRequestTimeEpoch() / 1000; } else if (httpApiContext != null && httpApiContext.getTimeEpoch() > 0) { timeEpoch = httpApiContext.getTimeEpoch() / 1000; diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletRequest.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletRequest.java new file mode 100644 index 000000000..3ff2ee0f3 --- /dev/null +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletRequest.java @@ -0,0 +1,453 @@ +package com.amazonaws.serverless.proxy.internal.servlet; + +import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; +import com.amazonaws.serverless.proxy.internal.SecurityUtils; +import com.amazonaws.serverless.proxy.model.ContainerConfig; +import com.amazonaws.serverless.proxy.model.Headers; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import jakarta.servlet.*; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpUpgradeHandler; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.SecurityContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.*; + +public class AwsAlbHttpServletRequest extends AwsHttpServletRequest { + + //------------------------------------------------------------- + // Variables - Private + //------------------------------------------------------------- + + private ApplicationLoadBalancerRequestEvent request; + private SecurityContext securityContext; + private AwsAsyncContext asyncContext; + private static Logger log = LoggerFactory.getLogger(AwsProxyHttpServletRequest.class); + private ContainerConfig config; + + public AwsAlbHttpServletRequest(ApplicationLoadBalancerRequestEvent albRequest, Context lambdaContext, SecurityContext awsSecurityContext) { + this(albRequest, lambdaContext, awsSecurityContext, LambdaContainerHandler.getContainerConfig()); + } + + public AwsAlbHttpServletRequest(ApplicationLoadBalancerRequestEvent albRequest, Context lambdaContext, SecurityContext awsSecurityContext, ContainerConfig config) { + super(lambdaContext); + this.request = albRequest; + this.securityContext = awsSecurityContext; + this.config = config; + } + + public ApplicationLoadBalancerRequestEvent getAwsProxyRequest() { + return this.request; + } + @Override + public String getAuthType() { + return securityContext.getAuthenticationScheme(); + } + + + @Override + public Cookie[] getCookies() { + return AwsHttpServletRequestHelper.getCookies(request.getMultiValueHeaders()); + } + + + @Override + public long getDateHeader(String s) { + return AwsHttpServletRequestHelper.getDateHeader(request.getMultiValueHeaders(), s, log); + } + + + @Override + public String getHeader(String s) { + return AwsHttpServletRequestHelper.getHeader( + request.getMultiValueHeaders(), + null, + null, + s, + getHeaderValues(s) + ); + } + + + @Override + public Enumeration getHeaders(String s) { + return AwsHttpServletRequestHelper.getHeaders(request.getMultiValueHeaders(), s); + } + + @Override + public Enumeration getHeaderNames() { + return AwsHttpServletRequestHelper.getHeaderNames(request.getMultiValueHeaders()); + } + + + @Override + public int getIntHeader(String s) { + return AwsHttpServletRequestHelper.getIntHeader(request.getMultiValueHeaders(), s); + } + + + @Override + public String getMethod() { + return request.getHttpMethod(); + } + + + @Override + public String getPathInfo() { + return AwsHttpServletRequestHelper.getPathInfo(request.getPath()); + } + + + @Override + public String getPathTranslated() { + return AwsHttpServletRequestHelper.getPathTranslated(); + } + + + @Override + public String getContextPath() { + return AwsHttpServletRequestHelper.getContextPath(config, null, this); + } + + + @Override + public String getQueryString() { + return AwsHttpServletRequestHelper.getQueryString(request.getMultiValueQueryStringParameters(), config, log, this); + } + + + @Override + public String getRemoteUser() { + return AwsHttpServletRequestHelper.getRemoteUser(securityContext); + } + + + @Override + public boolean isUserInRole(String s) { + // TODO: Not supported? + return false; + } + + + @Override + public Principal getUserPrincipal() { + return AwsHttpServletRequestHelper.getUserPrincipal(securityContext); + } + + + @Override + public String getRequestURI() { return AwsHttpServletRequestHelper.getRequestURI(request.getPath(), this);} + + + @Override + public StringBuffer getRequestURL() { + return AwsHttpServletRequestHelper.getRequestURL(request.getPath(), this); + } + + + @Override + public boolean authenticate(HttpServletResponse httpServletResponse) + throws IOException, ServletException { + throw new UnsupportedOperationException(); + } + + + @Override + public void login(String s, String s1) + throws ServletException { + throw new UnsupportedOperationException(); + } + + + @Override + public void logout() + throws ServletException { + throw new UnsupportedOperationException(); + } + + + @Override + public Collection getParts() + throws IOException, ServletException { + return AwsHttpServletRequestHelper.getParts(this); + } + + + @Override + public Part getPart(String s) + throws IOException, ServletException { + return AwsHttpServletRequestHelper.getPart(s, this); + } + + + @Override + public T upgrade(Class aClass) + throws IOException, ServletException { + throw new UnsupportedOperationException(); + } + + //------------------------------------------------------------- + // Implementation - ServletRequest + //------------------------------------------------------------- + + + @Override + public String getCharacterEncoding() { + return AwsHttpServletRequestHelper.getCharacterEncoding( + request.getMultiValueHeaders(), + config, + this + ); + } + + + @Override + public void setCharacterEncoding(String s) + throws UnsupportedEncodingException { + if (request.getMultiValueHeaders() == null) { + request.setMultiValueHeaders(new Headers()); + } + String currentContentType = getFirst(request.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE); + if (currentContentType == null || "".equals(currentContentType)) { + log.debug("Called set character encoding to " + SecurityUtils.crlf(s) + " on a request without a content type. Character encoding will not be set"); + return; + } + putSingle(request.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE, appendCharacterEncoding(currentContentType, s)); + } + + @Override + public int getContentLength() { + return AwsHttpServletRequestHelper.getContentLength(request.getMultiValueHeaders()); + } + + + @Override + public long getContentLengthLong() { + return AwsHttpServletRequestHelper.getContentLengthLong(request.getMultiValueHeaders()); + } + + + @Override + public String getContentType() { + return AwsHttpServletRequestHelper.getContentType(request.getMultiValueHeaders()); + } + + @Override + public String getParameter(String s) { + return AwsHttpServletRequestHelper.getParameter( + request.getMultiValueQueryStringParameters(), + s, + config, + this + ); + } + + + @Override + public Enumeration getParameterNames() { + return AwsHttpServletRequestHelper.getParameterNames( + request.getMultiValueQueryStringParameters(), + this + ); + } + + + @Override + @SuppressFBWarnings("PZLA_PREFER_ZERO_LENGTH_ARRAYS") // suppressing this as according to the specs we should be returning null here if we can't find params + public String[] getParameterValues(String s) { + return AwsHttpServletRequestHelper.getParameterValues( + request.getMultiValueQueryStringParameters(), + s, + config, + this + ); + } + + + @Override + public Map getParameterMap() { + return AwsHttpServletRequestHelper.getParameterMap( + request.getMultiValueQueryStringParameters(), + config, + this + ); + } + + + @Override + public String getProtocol() { + return ""; + } + + + @Override + public String getScheme() { + return AwsHttpServletRequestHelper.getScheme( + request.getMultiValueHeaders(), + this + ); + } + + @Override + public String getServerName() { + return AwsHttpServletRequestHelper.getServerName( + request.getMultiValueHeaders(), + null + ); + } + + @Override + public int getServerPort() { + return AwsHttpServletRequestHelper.getServerPort(request.getMultiValueHeaders()); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + return AwsHttpServletRequestHelper.getInputStream( + requestInputStream, + request.getBody(), + request.getIsBase64Encoded(), + this + ); + } + + + @Override + public BufferedReader getReader() + throws IOException { + return AwsHttpServletRequestHelper.getReader(request.getBody()); + } + + + @Override + public String getRemoteAddr() { + return ""; + } + + + @Override + public String getRemoteHost() { + return AwsHttpServletRequestHelper.getRemoteHost(request.getMultiValueHeaders()); + } + + + @Override + public Locale getLocale() { + return AwsHttpServletRequestHelper.getLocale( + request.getMultiValueHeaders(), + this + ); + } + + @Override + public Enumeration getLocales() { + return AwsHttpServletRequestHelper.getLocales( + request.getMultiValueHeaders(), + this + ); + } + + @Override + public boolean isSecure() { + return AwsHttpServletRequestHelper.isSecure(securityContext); + } + + + + @Override + public RequestDispatcher getRequestDispatcher(String s) { + return AwsHttpServletRequestHelper.getRequestDispatcher(s, this); + } + + + @Override + public int getRemotePort() { + return 0; + } + + + @Override + public boolean isAsyncSupported() { + return true; + } + + @Override + public boolean isAsyncStarted() { + if (asyncContext == null) { + return false; + } + if (asyncContext.isCompleted() || asyncContext.isDispatched()) { + return false; + } + return true; + } + + + @Override + public AsyncContext startAsync() + throws IllegalStateException { + asyncContext = new AwsAsyncContext(this, response, containerHandler); + setAttribute(DISPATCHER_TYPE_ATTRIBUTE, DispatcherType.ASYNC); + log.debug("Starting async context for request: " + SecurityUtils.crlf(request.getRequestContext().getElb().getTargetGroupArn())); + return asyncContext; + } + + + @Override + public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) + throws IllegalStateException { + return AwsHttpServletRequestHelper.startAsync( + asyncContext, + request.getRequestContext().getElb().getTargetGroupArn(), + log, + servletRequest, + servletResponse, + containerHandler + ); + } + + @Override + public AsyncContext getAsyncContext() { + return AwsHttpServletRequestHelper.getAsyncContext( + asyncContext, + request.getRequestContext().getElb().getTargetGroupArn() + ); + } + + @Override + public String getRequestId() { + return ""; + } + + @Override + public String getProtocolRequestId() { + return ""; + } + + @Override + public ServletConnection getServletConnection() { + return null; + } + + //------------------------------------------------------------- + // Methods - Private + //------------------------------------------------------------- + + private List getHeaderValues(String key) { + if (Objects.isNull(request.getMultiValueHeaders())) { + return null; + } + + return request.getMultiValueHeaders().get(key); + } +} diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletRequestReader.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletRequestReader.java new file mode 100644 index 000000000..a59f2bc4d --- /dev/null +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletRequestReader.java @@ -0,0 +1,78 @@ +package com.amazonaws.serverless.proxy.internal.servlet; + +import com.amazonaws.serverless.exceptions.InvalidRequestEventException; +import com.amazonaws.serverless.proxy.RequestReader; +import com.amazonaws.serverless.proxy.model.ContainerConfig; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.SecurityContext; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Simple implementation of the RequestReader interface that receives an ApplicationLoadBalancerRequestEvent + * object and uses it to initialize a AwsProxyHttpServletRequest object. + */ +public class AwsAlbHttpServletRequestReader extends RequestReader { + + static final String INVALID_REQUEST_ERROR = "The incoming event is not a valid request from an Application Load Balancer"; + + private ServletContext servletContext; + + //------------------------------------------------------------- + // Methods - Implementation + //------------------------------------------------------------- + public void setServletContext(ServletContext ctx) { + servletContext = ctx; + } + @Override + public HttpServletRequest readRequest(ApplicationLoadBalancerRequestEvent request, SecurityContext securityContext, Context lambdaContext, ContainerConfig config) throws InvalidRequestEventException { + // Expect the HTTP method and context to be populated. If they are not, we are handling an + // unsupported event type. + if (request.getHttpMethod() == null || request.getHttpMethod().equals("") || request.getRequestContext() == null) { + throw new InvalidRequestEventException(INVALID_REQUEST_ERROR); + } + + request.setPath(stripBasePath(request.getPath(), config)); + if (request.getMultiValueHeaders() != null && request.getMultiValueHeaders().get(HttpHeaders.CONTENT_TYPE) != null && request.getMultiValueHeaders().get(HttpHeaders.CONTENT_TYPE).get(0) != null) { + String contentType = request.getMultiValueHeaders().get(HttpHeaders.CONTENT_TYPE).get(0); + // put single as we always expect to have one and only one content type in a request. + request.getMultiValueHeaders().put(HttpHeaders.CONTENT_TYPE, new ArrayList<>(Arrays.asList(getContentTypeWithCharset(contentType, config)))); + } + AwsAlbHttpServletRequest servletRequest = new AwsAlbHttpServletRequest(request, lambdaContext, securityContext, config); + servletRequest.setServletContext(servletContext); + servletRequest.setAttribute(ALB_EVENT_PROPERTY, request); + servletRequest.setAttribute(ALB_CONTEXT_PROPERTY, request.getRequestContext()); + servletRequest.setAttribute(LAMBDA_CONTEXT_PROPERTY, lambdaContext); + servletRequest.setAttribute(JAX_SECURITY_CONTEXT_PROPERTY, securityContext); + + return servletRequest; + } + + @Override + protected Class getRequestClass() { + return ApplicationLoadBalancerRequestEvent.class; + } + + private String getContentTypeWithCharset(String headerValue, ContainerConfig config) { + if (headerValue == null || "".equals(headerValue.trim())) { + return headerValue; + } + + if (headerValue.contains("charset=")) { + return headerValue; + } + + String newValue = headerValue; + if (!headerValue.trim().endsWith(";")) { + newValue += "; "; + } + + newValue += "charset=" + config.getDefaultContentCharset(); + return newValue; + } +} diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletResponseWriter.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletResponseWriter.java new file mode 100644 index 000000000..4bbc34bb1 --- /dev/null +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletResponseWriter.java @@ -0,0 +1,83 @@ +package com.amazonaws.serverless.proxy.internal.servlet; + +import com.amazonaws.serverless.exceptions.InvalidResponseObjectException; +import com.amazonaws.serverless.proxy.ResponseWriter; +import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; +import com.amazonaws.serverless.proxy.internal.testutils.Timer; +import com.amazonaws.serverless.proxy.model.AwsProxyResponse; +import com.amazonaws.serverless.proxy.model.Headers; +import com.amazonaws.services.lambda.runtime.Context; +import jakarta.ws.rs.core.Response; + +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +public class AwsAlbHttpServletResponseWriter extends ResponseWriter { + + private boolean writeSingleValueHeaders; + + public AwsAlbHttpServletResponseWriter() { + this(false); + } + + public AwsAlbHttpServletResponseWriter(boolean singleValueHeaders) { + writeSingleValueHeaders = singleValueHeaders; + } + + @Override + public AwsProxyResponse writeResponse(AwsHttpServletResponse containerResponse, Context lambdaContext) throws InvalidResponseObjectException { + Timer.start("SERVLET_RESPONSE_WRITE"); + AwsProxyResponse awsProxyResponse = new AwsProxyResponse(); + if (containerResponse.getAwsResponseBodyString() != null) { + String responseString; + + if (!isBinary(containerResponse.getContentType()) && isValidUtf8(containerResponse.getAwsResponseBodyBytes())) { + responseString = containerResponse.getAwsResponseBodyString(); + } else { + responseString = Base64.getEncoder().encodeToString(containerResponse.getAwsResponseBodyBytes()); + awsProxyResponse.setBase64Encoded(true); + } + + awsProxyResponse.setBody(responseString); + } + awsProxyResponse.setMultiValueHeaders(containerResponse.getAwsResponseHeaders()); + if (writeSingleValueHeaders) { + awsProxyResponse.setHeaders(toSingleValueHeaders(containerResponse.getAwsResponseHeaders())); + } + + awsProxyResponse.setStatusCode(containerResponse.getStatus()); + + Response.Status responseStatus = Response.Status.fromStatusCode(containerResponse.getStatus()); + + if (containerResponse.getAwsAlbRequest() != null && responseStatus != null) { + awsProxyResponse.setStatusDescription(containerResponse.getStatus() + " " + responseStatus.getReasonPhrase()); + } + + Timer.stop("SERVLET_RESPONSE_WRITE"); + return awsProxyResponse; + } + + private Map toSingleValueHeaders(Headers h) { + Map out = new HashMap<>(); + if (h == null || h.isEmpty()) { + return out; + } + for (String k : h.keySet()) { + out.put(k, h.getFirst(k)); + } + return out; + } + + private boolean isBinary(String contentType) { + if (contentType != null) { + int semidx = contentType.indexOf(';'); + if (semidx >= 0) { + return LambdaContainerHandler.getContainerConfig().isBinaryContentType(contentType.substring(0, semidx)); + } else { + return LambdaContainerHandler.getContainerConfig().isBinaryContentType(contentType); + } + } + return false; + } +} \ No newline at end of file diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbSecurityContext.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbSecurityContext.java new file mode 100644 index 000000000..7b332ae12 --- /dev/null +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbSecurityContext.java @@ -0,0 +1,89 @@ +package com.amazonaws.serverless.proxy.internal.servlet; + + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; +import jakarta.ws.rs.core.SecurityContext; + +import java.security.Principal; + +public class AwsAlbSecurityContext implements SecurityContext { + + //------------------------------------------------------------- + // Constants - Package + //------------------------------------------------------------- + + static final String AUTH_SCHEME_CUSTOM = "CUSTOM_AUTHORIZER"; + static final String AUTH_SCHEME_AWS_IAM = "AWS_IAM"; + + static final String ALB_ACCESS_TOKEN_HEADER = "x-amzn-oidc-accesstoken"; + static final String ALB_IDENTITY_HEADER = "x-amzn-oidc-identity"; + + + //------------------------------------------------------------- + // Variables - Private + //------------------------------------------------------------- + + private Context lambdaContext; + private ApplicationLoadBalancerRequestEvent event; + + + public Context getLambdaContext() { + return lambdaContext; + } + + + public ApplicationLoadBalancerRequestEvent getEvent() { + return event; + } + + //------------------------------------------------------------- + // Constructors + //------------------------------------------------------------- + + public AwsAlbSecurityContext(final Context lambdaContext, final ApplicationLoadBalancerRequestEvent event) { + this.lambdaContext = lambdaContext; + this.event = event; + } + + //------------------------------------------------------------- + // Implementation - SecurityContext + //------------------------------------------------------------- + @Override + public Principal getUserPrincipal() { + if (getAuthenticationScheme() == null) { + return () -> null; + } + + if (getAuthenticationScheme().equals(AUTH_SCHEME_CUSTOM) || getAuthenticationScheme().equals(AUTH_SCHEME_AWS_IAM)) { + return () -> { + if (getAuthenticationScheme().equals(AUTH_SCHEME_CUSTOM) && event.getMultiValueHeaders().get(ALB_IDENTITY_HEADER) != null) { + return event.getMultiValueHeaders().get(ALB_IDENTITY_HEADER).get(0); + } + + // return null if we couldn't find a valid scheme + return null; + }; + } + + throw new RuntimeException("Cannot recognize authorization scheme in event"); + } + + @Override + public boolean isUserInRole(String role) { + return false; + } + + @Override + public boolean isSecure() { + return getAuthenticationScheme() != null; + } + + @Override + public String getAuthenticationScheme() { + if (event.getMultiValueHeaders().containsKey(ALB_ACCESS_TOKEN_HEADER)) { + return AUTH_SCHEME_CUSTOM; + } + return null; + } +} diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbSecurityContextWriter.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbSecurityContextWriter.java new file mode 100644 index 000000000..c54853f45 --- /dev/null +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbSecurityContextWriter.java @@ -0,0 +1,25 @@ +package com.amazonaws.serverless.proxy.internal.servlet; + +import com.amazonaws.serverless.proxy.SecurityContextWriter; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; +import jakarta.ws.rs.core.SecurityContext; + +public class AwsAlbSecurityContextWriter implements SecurityContextWriter { + + private AwsAlbSecurityContext currentContext; + + @Override + public SecurityContext writeSecurityContext(ApplicationLoadBalancerRequestEvent event, Context lambdaContext) { + currentContext = new AwsAlbSecurityContext(lambdaContext, event); + return currentContext; + } + + //------------------------------------------------------------- + // Methods - Getter/Setter + //------------------------------------------------------------- + + public AwsAlbSecurityContext getCurrentContext() { + return currentContext; + } +} diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletRequest.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletRequest.java index 7a44fa95e..aadce58fa 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletRequest.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletRequest.java @@ -16,11 +16,9 @@ import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; import com.amazonaws.serverless.proxy.internal.SecurityUtils; import com.amazonaws.serverless.proxy.internal.testutils.Timer; -import com.amazonaws.serverless.proxy.model.AwsProxyRequestContext; import com.amazonaws.serverless.proxy.model.ContainerConfig; -import com.amazonaws.serverless.proxy.model.Headers; -import com.amazonaws.serverless.proxy.model.MultiValuedTreeMap; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.apache.commons.fileupload2.core.FileItem; import org.apache.commons.fileupload2.core.FileUploadException; @@ -139,7 +137,7 @@ public String getRequestedSessionId() { public HttpSession getSession(boolean b) { log.debug("Trying to access session. Lambda functions are stateless and should not rely on the session"); if (b && null == this.session) { - AwsProxyRequestContext requestContext = (AwsProxyRequestContext) getAttribute(RequestReader.API_GATEWAY_CONTEXT_PROPERTY); + APIGatewayProxyRequestEvent.ProxyRequestContext requestContext = (APIGatewayProxyRequestEvent.ProxyRequestContext) getAttribute(RequestReader.API_GATEWAY_CONTEXT_PROPERTY); this.session = new AwsHttpSession(requestContext.getRequestId()); } return this.session; @@ -310,7 +308,7 @@ protected Cookie[] parseCookieHeaderValue(String headerValue) { * @param encodeCharset Charset to use for encoding the query string * @return The generated query string for the URI */ - protected String generateQueryString(MultiValuedTreeMap parameters, boolean encode, String encodeCharset) + protected String generateQueryString(Map> parameters, boolean encode, String encodeCharset) throws ServletException { if (parameters == null || parameters.size() == 0) { return null; @@ -442,15 +440,15 @@ protected ServletInputStream bodyStringToInputStream(String body, boolean isBase return new AwsServletInputStream(requestBodyStream); } - protected String getFirstQueryParamValue(MultiValuedTreeMap queryString, String key, boolean isCaseSensitive) { + protected String getFirstQueryParamValue(Map> queryString, String key, boolean isCaseSensitive) { if (queryString != null) { if (isCaseSensitive) { - return queryString.getFirst(key); + return getFirst(queryString, key); } for (String k : queryString.keySet()) { if (k.toLowerCase(Locale.getDefault()).equals(key.toLowerCase(Locale.getDefault()))) { - return queryString.getFirst(k); + return getFirst(queryString, k); } } } @@ -469,6 +467,29 @@ protected String[] getFormBodyParameterCaseInsensitive(String key) { } } + public static String getFirst(Map> map, String key) { + List values = map.get(key); + if (values == null || values.size() == 0) { + return null; + } + return values.get(0); + } + + public static void putSingle(Map> map, String key, String value) { + List values = findKey(map, key); + values.clear(); + values.add(value); + } + + public static List findKey(Map> map, String key) { + List values = map.get(key); + if (values == null) { + values = new ArrayList<>(); + map.put(key, values); + } + return values; + } + protected Map> getFormUrlEncodedParametersMap() { if (urlEncodedFormParameters != null) { @@ -547,7 +568,7 @@ protected Map getMultipartFormParametersMap() { return multipartFormParameters; } - protected String[] getQueryParamValues(MultiValuedTreeMap qs, String key, boolean isCaseSensitive) { + protected String[] getQueryParamValues(Map> qs, String key, boolean isCaseSensitive) { List value = getQueryParamValuesAsList(qs, key, isCaseSensitive); if (value == null){ return null; @@ -555,7 +576,7 @@ protected String[] getQueryParamValues(MultiValuedTreeMap qs, St return value.toArray(new String[0]); } - protected List getQueryParamValuesAsList(MultiValuedTreeMap qs, String key, boolean isCaseSensitive) { + protected List getQueryParamValuesAsList(Map> qs, String key, boolean isCaseSensitive) { if (qs != null) { if (isCaseSensitive) { return qs.get(key); @@ -571,7 +592,7 @@ protected List getQueryParamValuesAsList(MultiValuedTreeMap generateParameterMap(MultiValuedTreeMap qs, ContainerConfig config) { + protected Map generateParameterMap(Map> qs, ContainerConfig config) { Map output; Map> formEncodedParams = getFormUrlEncodedParametersMap(); @@ -608,16 +629,16 @@ protected Map generateParameterMap(MultiValuedTreeMap> headers) { // if we don't have any headers to deduce the value we assume HTTPS - API Gateway's default if (headers == null) { return "https"; } - String cfScheme = headers.getFirst(CF_PROTOCOL_HEADER_NAME); + String cfScheme = getFirst(headers, CF_PROTOCOL_HEADER_NAME); if (cfScheme != null && SecurityUtils.isValidScheme(cfScheme)) { return cfScheme; } - String gwScheme = headers.getFirst(PROTOCOL_HEADER_NAME); + String gwScheme = getFirst(headers, PROTOCOL_HEADER_NAME); if (gwScheme != null && SecurityUtils.isValidScheme(gwScheme)) { return gwScheme; } @@ -750,7 +771,7 @@ protected Locale parseLanguageTag(String languageTag) { return new Locale(language, country); } - static String decodeRequestPath(String requestPath, ContainerConfig config) { + public static String decodeRequestPath(String requestPath, ContainerConfig config) { try { return URLDecoder.decode(requestPath, config.getUriEncoding()); } catch (UnsupportedEncodingException ex) { @@ -761,7 +782,7 @@ static String decodeRequestPath(String requestPath, ContainerConfig config) { } - static String cleanUri(String uri) { + public static String cleanUri(String uri) { String finalUri = (uri == null ? "/" : uri); if (finalUri.equals("/")) { return finalUri; diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletRequestHelper.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletRequestHelper.java new file mode 100644 index 000000000..e5fdb123e --- /dev/null +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletRequestHelper.java @@ -0,0 +1,449 @@ +package com.amazonaws.serverless.proxy.internal.servlet; + +import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; +import com.amazonaws.serverless.proxy.internal.SecurityUtils; +import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequest; +import com.amazonaws.serverless.proxy.model.ContainerConfig; +import com.amazonaws.serverless.proxy.model.Headers; +import jakarta.servlet.*; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.SecurityContext; +import org.slf4j.Logger; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.time.Instant; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequest.cleanUri; +import static com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequest.decodeRequestPath; + +public class AwsHttpServletRequestHelper { + static final DateTimeFormatter dateFormatter = DateTimeFormatter.RFC_1123_DATE_TIME; + private static final String HEADER_KEY_VALUE_SEPARATOR = "="; + + public static Cookie[] getCookies(Map> headers) { + if (headers == null) { + return new Cookie[0]; + } + String cookieHeader = getFirst(headers, HttpHeaders.COOKIE); + if (cookieHeader == null) { + return new Cookie[0]; + } + return parseCookieHeaderValue(cookieHeader); + } + + public static long getDateHeader(Map> headers, String s, Logger log) { + if (headers == null) { + return -1L; + } + String dateString = getFirst(headers, s); + if (dateString == null) { + return -1L; + } + try { + return Instant.from(ZonedDateTime.parse(dateString, dateFormatter)).toEpochMilli(); + } catch (DateTimeParseException e) { + log.warn("Invalid date header in request" + SecurityUtils.crlf(dateString)); + return -1L; + + } + } + + public static String getHeader(Map> headers, String caller, String userAgent, String s, List headerValues) { + List values = headerValues; + if (values == null || values.size() == 0) { + return null; + } + return values.get(0); + } + + public static Enumeration getHeaders(Map> headers, String s) { + if (headers == null || headers.get(s) == null) { + return Collections.emptyEnumeration(); + } + return Collections.enumeration(headers.get(s)); + } + + public static Enumeration getHeaderNames(Map> headers) { + if (headers == null) { + return Collections.emptyEnumeration(); + } + return Collections.enumeration(headers.keySet()); + } + + public static int getIntHeader(Map> headers, String s) { + if (headers == null) { + return -1; + } + String headerValue = getFirst(headers, s); + if (headerValue == null) { + return -1; + } + + return Integer.parseInt(headerValue); + } + + public static String getPathInfo(String path) { + String pathInfo = cleanUri(path); + return decodeRequestPath(pathInfo, LambdaContainerHandler.getContainerConfig()); + } + + public static String getPathTranslated() { + // Return null because it is an archive on a remote system + return null; + } + + public static String getContextPath(ContainerConfig config, String stage, AwsHttpServletRequest servletRequest) { + return servletRequest.generateContextPath(config, stage); + } + + public static String getQueryString(Map> queryStrings, ContainerConfig config, Logger log, AwsHttpServletRequest servletRequest) { + try { + return servletRequest.generateQueryString( + queryStrings, + // ALB does not automatically decode parameters, so we don't want to re-encode them + true, + config.getUriEncoding()); + } catch (ServletException e) { + log.error("Could not generate query string", e); + return null; + } + } + + public static String getRemoteUser(SecurityContext securityContext) { + return securityContext.getUserPrincipal().getName(); + } + + public static Principal getUserPrincipal(SecurityContext securityContext) { + return securityContext.getUserPrincipal(); + } + + public static String getRequestURI(String path, AwsHttpServletRequest servletRequest) { + return cleanUri(servletRequest.getContextPath()) + cleanUri(path); + } + + public static StringBuffer getRequestURL(String path, AwsHttpServletRequest servletRequest) { + return servletRequest.generateRequestURL(path); + } + + public static Collection getParts(AwsHttpServletRequest servletRequest) + throws IOException, ServletException { + return servletRequest.getMultipartFormParametersMap().values(); + } + + public static Part getPart(String s, AwsHttpServletRequest servletRequest) + throws IOException, ServletException { + return servletRequest.getMultipartFormParametersMap().get(s); + } + + public static String getCharacterEncoding(Map> headers, ContainerConfig config, AwsHttpServletRequest servletRequest) { + if (headers == null) { + return config.getDefaultContentCharset(); + } + return servletRequest.parseCharacterEncoding(getFirst(headers, HttpHeaders.CONTENT_TYPE)); + } + + public static int getContentLength(Map> headers) { + String headerValue = getFirst(headers, HttpHeaders.CONTENT_LENGTH); + if (headerValue == null) { + return -1; + } + return Integer.parseInt(headerValue); + } + + public static long getContentLengthLong(Map> headers) { + String headerValue = getFirst(headers, HttpHeaders.CONTENT_LENGTH); + if (headerValue == null) { + return -1; + } + return Long.parseLong(headerValue); + } + + public static String getContentType(Map> headers) { + String contentTypeHeader = getFirst(headers, HttpHeaders.CONTENT_TYPE); + if (contentTypeHeader == null || "".equals(contentTypeHeader.trim())) { + return null; + } + + return contentTypeHeader; + } + + public static String getParameter(Map> queryStrings, String s, ContainerConfig config, AwsHttpServletRequest servletRequest) { + String queryStringParameter = servletRequest.getFirstQueryParamValue(queryStrings, s, config.isQueryStringCaseSensitive()); + if (queryStringParameter != null) { + return queryStringParameter; + } + + String[] bodyParams = servletRequest.getFormBodyParameterCaseInsensitive(s); + if (bodyParams.length == 0) { + return null; + } else { + return bodyParams[0]; + } + } + + public static Enumeration getParameterNames(Map> queryStrings, AwsHttpServletRequest servletRequest) { + Set formParameterNames = servletRequest.getFormUrlEncodedParametersMap().keySet(); + if (queryStrings == null) { + return Collections.enumeration(formParameterNames); + } + return Collections.enumeration(Stream.concat(formParameterNames.stream(), + queryStrings.keySet().stream()).collect(Collectors.toSet())); + } + + public static String[] getParameterValues(Map> queryStrings, String s, ContainerConfig config, AwsHttpServletRequest servletRequest) { + List values = new ArrayList<>(Arrays.asList(servletRequest.getQueryParamValues(queryStrings, s, config.isQueryStringCaseSensitive()))); + + values.addAll(Arrays.asList(servletRequest.getFormBodyParameterCaseInsensitive(s))); + + if (values.size() == 0) { + return null; + } else { + return values.toArray(new String[0]); + } + } + + public static Map getParameterMap(Map> queryStrings, ContainerConfig config, AwsHttpServletRequest servletRequest) { + return servletRequest.generateParameterMap(queryStrings, config); + } + + public static String getScheme(Map> headers, AwsHttpServletRequest servletRequest) { + return servletRequest.getSchemeFromHeader(headers); + } + + public static String getServerName(Map> headers, String apiId) { + String region = System.getenv("AWS_REGION"); + if (region == null) { + // this is not a critical failure, we just put a static region in the URI + region = "us-east-1"; + } + + if (headers != null && headers.containsKey(AwsHttpServletRequest.HOST_HEADER_NAME)) { + String hostHeader = getFirst(headers, AwsHttpServletRequest.HOST_HEADER_NAME); + if (SecurityUtils.isValidHost(hostHeader, apiId, region)) { + return hostHeader; + } + } + + return new StringBuilder().append(apiId) + .append(".execute-api.") + .append(region) + .append(".amazonaws.com").toString(); + } + + public static int getServerPort(Map> headers) { + if (headers == null) { + return 443; + } + String port = getFirst(headers, AwsHttpServletRequest.PORT_HEADER_NAME); + if (SecurityUtils.isValidPort(port)) { + return Integer.parseInt(port); + } else { + return 443; // default port + } + } + + public static ServletInputStream getInputStream(ServletInputStream requestInputStream, String body, boolean isBase64Encoded, AwsHttpServletRequest servletRequest) throws IOException { + if (requestInputStream == null) { + requestInputStream = new AwsServletInputStream(servletRequest.bodyStringToInputStream(body, isBase64Encoded)); + } + return requestInputStream; + } + + public static BufferedReader getReader(String body) + throws IOException { + return new BufferedReader(new StringReader(body)); + } + + public static String getRemoteHost(Map> headers) { + return getFirst(headers, HttpHeaders.HOST); + } + + public static Locale getLocale(Map> headers, AwsHttpServletRequest servletRequest) { + List locales = servletRequest.parseAcceptLanguageHeader(getFirst(headers, HttpHeaders.ACCEPT_LANGUAGE)); + return locales.size() == 0 ? Locale.getDefault() : locales.get(0); + } + + public static Enumeration getLocales(Map> headers, AwsHttpServletRequest servletRequest) { + List locales = servletRequest.parseAcceptLanguageHeader(getFirst(headers, HttpHeaders.ACCEPT_LANGUAGE)); + return Collections.enumeration(locales); + } + + public static boolean isSecure(SecurityContext securityContext) { + return securityContext.isSecure(); + } + + public static RequestDispatcher getRequestDispatcher(String s, AwsHttpServletRequest servletRequest) { + return servletRequest.getServletContext().getRequestDispatcher(s); + } + + public static boolean isAsyncStarted(AwsAsyncContext asyncContext) { + if (asyncContext == null) { + return false; + } + if (asyncContext.isCompleted() || asyncContext.isDispatched()) { + return false; + } + return true; + } + + public static AsyncContext startAsync(AwsAsyncContext asyncContext, String requestId, Logger log, AwsHttpServletResponse response, AwsLambdaServletContainerHandler containerHandler, AwsHttpServletRequest servletRequest) + throws IllegalStateException { + asyncContext = new AwsAsyncContext(servletRequest, response, containerHandler); + servletRequest.setAttribute(AwsHttpServletRequest.DISPATCHER_TYPE_ATTRIBUTE, DispatcherType.ASYNC); + log.debug("Starting async context for request: " + SecurityUtils.crlf(requestId)); + return asyncContext; + } + + + public static AsyncContext startAsync(AwsAsyncContext asyncContext, String requestId, Logger log, ServletRequest servletRequest, ServletResponse servletResponse, AwsLambdaServletContainerHandler containerHandler) + throws IllegalStateException { + servletRequest.setAttribute(AwsHttpServletRequest.DISPATCHER_TYPE_ATTRIBUTE, DispatcherType.ASYNC); + asyncContext = new AwsAsyncContext((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse, containerHandler); + log.debug("Starting async context for request: " + SecurityUtils.crlf(requestId)); + return asyncContext; + } + + public static AsyncContext getAsyncContext(AwsAsyncContext asyncContext, String requestId) { + if (asyncContext == null) { + throw new IllegalStateException("Request " + SecurityUtils.crlf(requestId) + + " is not in asynchronous mode. Call startAsync before attempting to get the async context."); + } + return asyncContext; + } + + + + + public static String getFirst(Map> map, String key) { + List values = map.get(key); + if (values == null || values.size() == 0) { + return null; + } + return values.get(0); + } + + public static void putSingle(Map> map, String key, String value) { + List values = findKey(map, key); + values.clear(); + values.add(value); + } + + public static List findKey(Map> map, String key) { + List values = map.get(key); + if (values == null) { + values = new ArrayList<>(); + map.put(key, values); + } + return values; + } + + /** + * Given the Cookie header value, parses it and creates a Cookie object + * @param headerValue The string value of the HTTP Cookie header + * @return An array of Cookie objects from the header + */ + protected static Cookie[] parseCookieHeaderValue(String headerValue) { + List parsedHeaders = parseHeaderValue(headerValue, ";", ","); + + return parsedHeaders.stream() + .filter(e -> e.getKey() != null) + .map(e -> new Cookie(SecurityUtils.crlf(e.getKey()), SecurityUtils.crlf(e.getValue()))) + .toArray(Cookie[]::new); + } + + /** + * Generic method to parse an HTTP header value and split it into a list of key/values for all its components. + * When the property in the header does not specify a key the key field in the output pair is null and only the value + * is populated. For example, The header Accept: application/json; application/xml will contain two + * key value pairs with key null and the value set to application/json and application/xml respectively. + * + * @param headerValue The string value for the HTTP header + * @param valueSeparator The separator to be used for parsing header values + * @return A list of SimpleMapEntry objects with all of the possible values for the header. + */ + protected static List parseHeaderValue(String headerValue, String valueSeparator, String qualifierSeparator) { + // Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8 + // Accept-Language: fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5 + // Cookie: name=value; name2=value2; name3=value3 + // X-Custom-Header: YQ== + + List values = new ArrayList<>(); + if (headerValue == null) { + return values; + } + + for (String v : headerValue.split(valueSeparator)) { + String curValue = v; + float curPreference = 1.0f; + AwsHttpServletRequest.HeaderValue newValue = new AwsHttpServletRequest.HeaderValue(); + newValue.setRawValue(v); + + for (String q : curValue.split(qualifierSeparator)) { + + String[] kv = q.split(HEADER_KEY_VALUE_SEPARATOR, 2); + String key = null; + String val = null; + // no separator, set the value only + if (kv.length == 1) { + val = q.trim(); + } + // we have a separator + if (kv.length == 2) { + // if the length of the value is 0 we assume that we are looking at a + // base64 encoded value with padding so we just set the value. This is because + // we assume that empty values in a key/value pair will contain at least a white space + if (kv[1].length() == 0) { + val = q.trim(); + } + // this was a base64 string with an additional = for padding, set the value only + if ("=".equals(kv[1].trim())) { + val = q.trim(); + } else { // it's a proper key/value set both + key = kv[0].trim(); + val = ("".equals(kv[1].trim()) ? null : kv[1].trim()); + } + } + + if (newValue.getValue() == null) { + newValue.setKey(key); + newValue.setValue(val); + } else { + // special case for quality q= + if ("q".equals(key)) { + curPreference = Float.parseFloat(val); + } else { + newValue.addAttribute(key, val); + } + } + } + newValue.setPriority(curPreference); + values.add(newValue); + } + + // sort list by preference + values.sort((AwsHttpServletRequest.HeaderValue first, AwsHttpServletRequest.HeaderValue second) -> { + if ((first.getPriority() - second.getPriority()) < .001f) { + return 0; + } + if (first.getPriority() < second.getPriority()) { + return 1; + } + return -1; + }); + return values; + } +} diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletResponse.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletResponse.java index f82d062a7..2b1051528 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletResponse.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletResponse.java @@ -14,9 +14,10 @@ import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; import com.amazonaws.serverless.proxy.internal.SecurityUtils; -import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.Headers; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,6 +39,7 @@ import java.util.*; import java.util.concurrent.CountDownLatch; +import static com.amazonaws.serverless.proxy.RequestReader.ALB_EVENT_PROPERTY; import static com.amazonaws.serverless.proxy.RequestReader.API_GATEWAY_EVENT_PROPERTY; @@ -484,6 +486,11 @@ String getAwsResponseBodyString() { return responseBody; } + /* This is added for test purposes **/ + void setAwsResponseBodyString(String bodyString) { + responseBody = bodyString; + } + byte[] getAwsResponseBodyBytes() { if (bodyOutputStream != null) { return bodyOutputStream.toByteArray(); @@ -496,10 +503,13 @@ Headers getAwsResponseHeaders() { return headers; } - AwsProxyRequest getAwsProxyRequest() { - return (AwsProxyRequest)request.getAttribute(API_GATEWAY_EVENT_PROPERTY); + APIGatewayProxyRequestEvent getAwsProxyRequest() { + return (APIGatewayProxyRequestEvent)request.getAttribute(API_GATEWAY_EVENT_PROPERTY); } + ApplicationLoadBalancerRequestEvent getAwsAlbRequest() { + return (ApplicationLoadBalancerRequestEvent)request.getAttribute(ALB_EVENT_PROPERTY); + } //------------------------------------------------------------- // Methods - Private diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequest.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequest.java index a4ee15150..7a59f9831 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequest.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequest.java @@ -15,11 +15,10 @@ import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; import com.amazonaws.serverless.proxy.internal.SecurityUtils; -import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.ContainerConfig; import com.amazonaws.serverless.proxy.model.Headers; -import com.amazonaws.serverless.proxy.model.RequestSource; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,8 +41,8 @@ import java.util.stream.Stream; /** - * Implementation of the HttpServletRequest interface that supports AwsProxyRequest object. - * This object is initialized with an AwsProxyRequest event and a SecurityContext generated + * Implementation of the HttpServletRequest interface that supports APIGatewayProxyRequestEvent object. + * This object is initialized with an APIGatewayProxyRequestEvent event and a SecurityContext generated * by an implementation of the SecurityContextWriter. */ public class AwsProxyHttpServletRequest extends AwsHttpServletRequest { @@ -52,7 +51,7 @@ public class AwsProxyHttpServletRequest extends AwsHttpServletRequest { // Variables - Private //------------------------------------------------------------- - private AwsProxyRequest request; + private APIGatewayProxyRequestEvent request; private SecurityContext securityContext; private AwsAsyncContext asyncContext; private static Logger log = LoggerFactory.getLogger(AwsProxyHttpServletRequest.class); @@ -63,19 +62,19 @@ public class AwsProxyHttpServletRequest extends AwsHttpServletRequest { //------------------------------------------------------------- - public AwsProxyHttpServletRequest(AwsProxyRequest awsProxyRequest, Context lambdaContext, SecurityContext awsSecurityContext) { + public AwsProxyHttpServletRequest(APIGatewayProxyRequestEvent awsProxyRequest, Context lambdaContext, SecurityContext awsSecurityContext) { this(awsProxyRequest, lambdaContext, awsSecurityContext, LambdaContainerHandler.getContainerConfig()); } - public AwsProxyHttpServletRequest(AwsProxyRequest awsProxyRequest, Context lambdaContext, SecurityContext awsSecurityContext, ContainerConfig config) { + public AwsProxyHttpServletRequest(APIGatewayProxyRequestEvent awsProxyRequest, Context lambdaContext, SecurityContext awsSecurityContext, ContainerConfig config) { super(lambdaContext); this.request = awsProxyRequest; this.securityContext = awsSecurityContext; this.config = config; } - public AwsProxyRequest getAwsProxyRequest() { + public APIGatewayProxyRequestEvent getAwsProxyRequest() { return this.request; } @@ -92,74 +91,43 @@ public String getAuthType() { @Override public Cookie[] getCookies() { - if (request.getMultiValueHeaders() == null) { - return new Cookie[0]; - } - String cookieHeader = request.getMultiValueHeaders().getFirst(HttpHeaders.COOKIE); - if (cookieHeader == null) { - return new Cookie[0]; - } - return this.parseCookieHeaderValue(cookieHeader); + return AwsHttpServletRequestHelper.getCookies(request.getMultiValueHeaders()); } @Override public long getDateHeader(String s) { - if (request.getMultiValueHeaders() == null) { - return -1L; - } - String dateString = request.getMultiValueHeaders().getFirst(s); - if (dateString == null) { - return -1L; - } - try { - return Instant.from(ZonedDateTime.parse(dateString, dateFormatter)).toEpochMilli(); - } catch (DateTimeParseException e) { - log.warn("Invalid date header in request" + SecurityUtils.crlf(dateString)); - return -1L; - } + return AwsHttpServletRequestHelper.getDateHeader(request.getMultiValueHeaders(), s, log); } @Override public String getHeader(String s) { - List values = getHeaderValues(s); - if (values == null || values.size() == 0) { - return null; - } - return values.get(0); + return AwsHttpServletRequestHelper.getHeader( + request.getMultiValueHeaders(), + request.getRequestContext().getIdentity().getCaller(), + request.getRequestContext().getIdentity().getUserAgent(), + s, + getHeaderValues(s) + ); } @Override public Enumeration getHeaders(String s) { - if (request.getMultiValueHeaders() == null || request.getMultiValueHeaders().get(s) == null) { - return Collections.emptyEnumeration(); - } - return Collections.enumeration(request.getMultiValueHeaders().get(s)); + return AwsHttpServletRequestHelper.getHeaders(request.getMultiValueHeaders(), s); } @Override public Enumeration getHeaderNames() { - if (request.getMultiValueHeaders() == null) { - return Collections.emptyEnumeration(); - } - return Collections.enumeration(request.getMultiValueHeaders().keySet()); + return AwsHttpServletRequestHelper.getHeaderNames(request.getMultiValueHeaders()); } @Override public int getIntHeader(String s) { - if (request.getMultiValueHeaders() == null) { - return -1; - } - String headerValue = request.getMultiValueHeaders().getFirst(s); - if (headerValue == null) { - return -1; - } - - return Integer.parseInt(headerValue); + return AwsHttpServletRequestHelper.getIntHeader(request.getMultiValueHeaders(), s); } @@ -171,42 +139,31 @@ public String getMethod() { @Override public String getPathInfo() { - String pathInfo = cleanUri(request.getPath()); - return decodeRequestPath(pathInfo, LambdaContainerHandler.getContainerConfig()); + return AwsHttpServletRequestHelper.getPathInfo(request.getPath()); } @Override public String getPathTranslated() { - // Return null because it is an archive on a remote system - return null; + return AwsHttpServletRequestHelper.getPathTranslated(); } @Override public String getContextPath() { - return generateContextPath(config, request.getRequestContext().getStage()); + return AwsHttpServletRequestHelper.getContextPath(config, request.getRequestContext().getStage(), this); } @Override public String getQueryString() { - try { - return this.generateQueryString( - request.getMultiValueQueryStringParameters(), - // ALB does not automatically decode parameters, so we don't want to re-encode them - request.getRequestSource() != RequestSource.ALB, - config.getUriEncoding()); - } catch (ServletException e) { - log.error("Could not generate query string", e); - return null; - } + return AwsHttpServletRequestHelper.getQueryString(request.getMultiValueQueryStringParameters(), config, log, this); } @Override public String getRemoteUser() { - return securityContext.getUserPrincipal().getName(); + return AwsHttpServletRequestHelper.getRemoteUser(securityContext); } @@ -219,19 +176,19 @@ public boolean isUserInRole(String s) { @Override public Principal getUserPrincipal() { - return securityContext.getUserPrincipal(); + return AwsHttpServletRequestHelper.getUserPrincipal(securityContext); } @Override public String getRequestURI() { - return cleanUri(getContextPath()) + cleanUri(request.getPath()); + return AwsHttpServletRequestHelper.getRequestURI(request.getPath(), this); } @Override public StringBuffer getRequestURL() { - return generateRequestURL(request.getPath()); + return AwsHttpServletRequestHelper.getRequestURL(request.getPath(), this); } @@ -259,7 +216,7 @@ public void logout() @Override public Collection getParts() throws IOException, ServletException { - return getMultipartFormParametersMap().values(); + return AwsHttpServletRequestHelper.getParts(this); } @@ -283,10 +240,11 @@ public T upgrade(Class aClass) @Override public String getCharacterEncoding() { - if (request.getMultiValueHeaders() == null) { - return config.getDefaultContentCharset(); - } - return parseCharacterEncoding(request.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE)); + return AwsHttpServletRequestHelper.getCharacterEncoding( + request.getMultiValueHeaders(), + config, + this + ); } @@ -296,91 +254,72 @@ public void setCharacterEncoding(String s) if (request.getMultiValueHeaders() == null) { request.setMultiValueHeaders(new Headers()); } - String currentContentType = request.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE); + String currentContentType = getFirst(request.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE); if (currentContentType == null || "".equals(currentContentType)) { log.debug("Called set character encoding to " + SecurityUtils.crlf(s) + " on a request without a content type. Character encoding will not be set"); return; } - request.getMultiValueHeaders().putSingle(HttpHeaders.CONTENT_TYPE, appendCharacterEncoding(currentContentType, s)); + putSingle(request.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE, appendCharacterEncoding(currentContentType, s)); } @Override public int getContentLength() { - String headerValue = request.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_LENGTH); - if (headerValue == null) { - return -1; - } - return Integer.parseInt(headerValue); + return AwsHttpServletRequestHelper.getContentLength(request.getMultiValueHeaders()); } @Override public long getContentLengthLong() { - String headerValue = request.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_LENGTH); - if (headerValue == null) { - return -1; - } - return Long.parseLong(headerValue); + return AwsHttpServletRequestHelper.getContentLengthLong(request.getMultiValueHeaders()); } @Override public String getContentType() { - String contentTypeHeader = request.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE); - if (contentTypeHeader == null || "".equals(contentTypeHeader.trim())) { - return null; - } - - return contentTypeHeader; + return AwsHttpServletRequestHelper.getContentType(request.getMultiValueHeaders()); } @Override public String getParameter(String s) { - String queryStringParameter = getFirstQueryParamValue(request.getMultiValueQueryStringParameters(), s, config.isQueryStringCaseSensitive()); - if (queryStringParameter != null) { - return queryStringParameter; - } - - String[] bodyParams = getFormBodyParameterCaseInsensitive(s); - if (bodyParams.length == 0) { - return null; - } else { - return bodyParams[0]; - } + return AwsHttpServletRequestHelper.getParameter( + request.getMultiValueQueryStringParameters(), + s, + config, + this + ); } @Override public Enumeration getParameterNames() { - Set formParameterNames = getFormUrlEncodedParametersMap().keySet(); - if (request.getMultiValueQueryStringParameters() == null) { - return Collections.enumeration(formParameterNames); - } - return Collections.enumeration(Stream.concat(formParameterNames.stream(), - request.getMultiValueQueryStringParameters().keySet().stream()).collect(Collectors.toSet())); + return AwsHttpServletRequestHelper.getParameterNames( + request.getMultiValueQueryStringParameters(), + this + ); } @Override @SuppressFBWarnings("PZLA_PREFER_ZERO_LENGTH_ARRAYS") // suppressing this as according to the specs we should be returning null here if we can't find params public String[] getParameterValues(String s) { - List values = new ArrayList<>(Arrays.asList(getQueryParamValues(request.getMultiValueQueryStringParameters(), s, config.isQueryStringCaseSensitive()))); - - values.addAll(Arrays.asList(getFormBodyParameterCaseInsensitive(s))); - - if (values.size() == 0) { - return null; - } else { - return values.toArray(new String[0]); - } + return AwsHttpServletRequestHelper.getParameterValues( + request.getMultiValueQueryStringParameters(), + s, + config, + this + ); } @Override public Map getParameterMap() { - return generateParameterMap(request.getMultiValueQueryStringParameters(), config); + return AwsHttpServletRequestHelper.getParameterMap( + request.getMultiValueQueryStringParameters(), + config, + this + ); } @@ -392,56 +331,40 @@ public String getProtocol() { @Override public String getScheme() { - return getSchemeFromHeader(request.getMultiValueHeaders()); + return AwsHttpServletRequestHelper.getScheme( + request.getMultiValueHeaders(), + this + ); } @Override public String getServerName() { - String region = System.getenv("AWS_REGION"); - if (region == null) { - // this is not a critical failure, we just put a static region in the URI - region = "us-east-1"; - } - - if (request.getMultiValueHeaders() != null && request.getMultiValueHeaders().containsKey(HOST_HEADER_NAME)) { - String hostHeader = request.getMultiValueHeaders().getFirst(HOST_HEADER_NAME); - if (SecurityUtils.isValidHost(hostHeader, request.getRequestContext().getApiId(), region)) { - return hostHeader; - } - } - - return new StringBuilder().append(request.getRequestContext().getApiId()) - .append(".execute-api.") - .append(region) - .append(".amazonaws.com").toString(); + return AwsHttpServletRequestHelper.getServerName( + request.getMultiValueHeaders(), + request.getRequestContext().getApiId() + ); } @Override public int getServerPort() { - if (request.getMultiValueHeaders() == null) { - return 443; - } - String port = request.getMultiValueHeaders().getFirst(PORT_HEADER_NAME); - if (SecurityUtils.isValidPort(port)) { - return Integer.parseInt(port); - } else { - return 443; // default port - } + return AwsHttpServletRequestHelper.getServerPort(request.getMultiValueHeaders()); } @Override public ServletInputStream getInputStream() throws IOException { - if (requestInputStream == null) { - requestInputStream = new AwsServletInputStream(bodyStringToInputStream(request.getBody(), request.isBase64Encoded())); - } - return requestInputStream; + return AwsHttpServletRequestHelper.getInputStream( + requestInputStream, + request.getBody(), + request.getIsBase64Encoded(), + this + ); } @Override public BufferedReader getReader() throws IOException { - return new BufferedReader(new StringReader(request.getBody())); + return AwsHttpServletRequestHelper.getReader(request.getBody()); } @@ -456,31 +379,35 @@ public String getRemoteAddr() { @Override public String getRemoteHost() { - return request.getMultiValueHeaders().getFirst(HttpHeaders.HOST); + return AwsHttpServletRequestHelper.getRemoteHost(request.getMultiValueHeaders()); } @Override public Locale getLocale() { - List locales = parseAcceptLanguageHeader(request.getMultiValueHeaders().getFirst(HttpHeaders.ACCEPT_LANGUAGE)); - return locales.size() == 0 ? Locale.getDefault() : locales.get(0); + return AwsHttpServletRequestHelper.getLocale( + request.getMultiValueHeaders(), + this + ); } @Override public Enumeration getLocales() { - List locales = parseAcceptLanguageHeader(request.getMultiValueHeaders().getFirst(HttpHeaders.ACCEPT_LANGUAGE)); - return Collections.enumeration(locales); + return AwsHttpServletRequestHelper.getLocales( + request.getMultiValueHeaders(), + this + ); } @Override public boolean isSecure() { - return securityContext.isSecure(); + return AwsHttpServletRequestHelper.isSecure(securityContext); } @Override public RequestDispatcher getRequestDispatcher(String s) { - return getServletContext().getRequestDispatcher(s); + return AwsHttpServletRequestHelper.getRequestDispatcher(s, this); } @@ -558,15 +485,13 @@ private List getHeaderValues(String key) { // special cases for referer and user agent headers List values = new ArrayList<>(); - if (request.getRequestSource() == RequestSource.API_GATEWAY) { - if ("referer".equals(key.toLowerCase(Locale.ENGLISH))) { - values.add(request.getRequestContext().getIdentity().getCaller()); - return values; - } - if ("user-agent".equals(key.toLowerCase(Locale.ENGLISH))) { - values.add(request.getRequestContext().getIdentity().getUserAgent()); - return values; - } + if ("referer".equals(key.toLowerCase(Locale.ENGLISH))) { + values.add(request.getRequestContext().getIdentity().getCaller()); + return values; + } + if ("user-agent".equals(key.toLowerCase(Locale.ENGLISH))) { + values.add(request.getRequestContext().getIdentity().getUserAgent()); + return values; } if (request.getMultiValueHeaders() == null) { diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestReader.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestReader.java index ec56285f7..66864a9ac 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestReader.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestReader.java @@ -14,20 +14,23 @@ import com.amazonaws.serverless.exceptions.InvalidRequestEventException; import com.amazonaws.serverless.proxy.RequestReader; -import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.ContainerConfig; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import jakarta.servlet.ServletContext; import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.SecurityContext; +import java.util.ArrayList; +import java.util.Arrays; + /** - * Simple implementation of the RequestReader interface that receives an AwsProxyRequest + * Simple implementation of the RequestReader interface that receives an APIGatewayProxyRequestEvent * object and uses it to initialize a AwsProxyHttpServletRequest object. */ -public class AwsProxyHttpServletRequestReader extends RequestReader { +public class AwsProxyHttpServletRequestReader extends RequestReader { static final String INVALID_REQUEST_ERROR = "The incoming event is not a valid request from Amazon API Gateway or an Application Load Balancer"; private ServletContext servletContext; @@ -40,7 +43,7 @@ public void setServletContext(ServletContext ctx) { } @Override - public HttpServletRequest readRequest(AwsProxyRequest request, SecurityContext securityContext, Context lambdaContext, ContainerConfig config) + public HttpServletRequest readRequest(APIGatewayProxyRequestEvent request, SecurityContext securityContext, Context lambdaContext, ContainerConfig config) throws InvalidRequestEventException { // Expect the HTTP method and context to be populated. If they are not, we are handling an // unsupported event type. @@ -49,17 +52,17 @@ public HttpServletRequest readRequest(AwsProxyRequest request, SecurityContext s } request.setPath(stripBasePath(request.getPath(), config)); - if (request.getMultiValueHeaders() != null && request.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE) != null) { - String contentType = request.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE); + if (request.getMultiValueHeaders() != null && request.getMultiValueHeaders().get(HttpHeaders.CONTENT_TYPE) != null && + request.getMultiValueHeaders().get(HttpHeaders.CONTENT_TYPE).get(0) != null) { + String contentType = request.getMultiValueHeaders().get(HttpHeaders.CONTENT_TYPE).get(0); // put single as we always expect to have one and only one content type in a request. - request.getMultiValueHeaders().putSingle(HttpHeaders.CONTENT_TYPE, getContentTypeWithCharset(contentType, config)); + request.getMultiValueHeaders().put(HttpHeaders.CONTENT_TYPE, new ArrayList<>(Arrays.asList(getContentTypeWithCharset(contentType, config)))); } AwsProxyHttpServletRequest servletRequest = new AwsProxyHttpServletRequest(request, lambdaContext, securityContext, config); servletRequest.setServletContext(servletContext); servletRequest.setAttribute(API_GATEWAY_CONTEXT_PROPERTY, request.getRequestContext()); servletRequest.setAttribute(API_GATEWAY_STAGE_VARS_PROPERTY, request.getStageVariables()); servletRequest.setAttribute(API_GATEWAY_EVENT_PROPERTY, request); - servletRequest.setAttribute(ALB_CONTEXT_PROPERTY, request.getRequestContext().getElb()); servletRequest.setAttribute(LAMBDA_CONTEXT_PROPERTY, lambdaContext); servletRequest.setAttribute(JAX_SECURITY_CONTEXT_PROPERTY, securityContext); @@ -71,8 +74,8 @@ public HttpServletRequest readRequest(AwsProxyRequest request, SecurityContext s //------------------------------------------------------------- @Override - protected Class getRequestClass() { - return AwsProxyRequest.class; + protected Class getRequestClass() { + return APIGatewayProxyRequestEvent.class; } //------------------------------------------------------------- diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletResponseWriter.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletResponseWriter.java index 0e230e9a7..ddbe1858a 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletResponseWriter.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletResponseWriter.java @@ -19,12 +19,8 @@ import com.amazonaws.serverless.proxy.internal.testutils.Timer; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; import com.amazonaws.serverless.proxy.model.Headers; -import com.amazonaws.serverless.proxy.model.RequestSource; import com.amazonaws.services.lambda.runtime.Context; -import jakarta.ws.rs.core.Response; -import jakarta.ws.rs.core.Response.Status; - import java.util.Base64; import java.util.HashMap; import java.util.Map; @@ -74,13 +70,6 @@ public AwsProxyResponse writeResponse(AwsHttpServletResponse containerResponse, awsProxyResponse.setStatusCode(containerResponse.getStatus()); - Status responseStatus = Response.Status.fromStatusCode(containerResponse.getStatus()); - - if (containerResponse.getAwsProxyRequest() != null && containerResponse.getAwsProxyRequest().getRequestSource() == RequestSource.ALB - && responseStatus != null) { - awsProxyResponse.setStatusDescription(containerResponse.getStatus() + " " + responseStatus.getReasonPhrase()); - } - Timer.stop("SERVLET_RESPONSE_WRITE"); return awsProxyResponse; } diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyRequestDispatcher.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyRequestDispatcher.java index c84424b05..a305884fb 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyRequestDispatcher.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyRequestDispatcher.java @@ -13,7 +13,7 @@ package com.amazonaws.serverless.proxy.internal.servlet; import com.amazonaws.serverless.proxy.internal.SecurityUtils; -import com.amazonaws.serverless.proxy.model.AwsProxyRequest; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.slf4j.Logger; @@ -133,8 +133,8 @@ void setRequestPath(ServletRequest req, final String destinationPath) { } log.debug("Request is not an proxy request generated by this library, attempting to extract the proxy event type from the request attributes"); - if (req.getAttribute(API_GATEWAY_EVENT_PROPERTY) != null && req.getAttribute(API_GATEWAY_EVENT_PROPERTY) instanceof AwsProxyRequest) { - ((AwsProxyRequest)req.getAttribute(API_GATEWAY_EVENT_PROPERTY)).setPath(dispatchTo); + if (req.getAttribute(API_GATEWAY_EVENT_PROPERTY) != null && req.getAttribute(API_GATEWAY_EVENT_PROPERTY) instanceof APIGatewayProxyRequestEvent) { + ((APIGatewayProxyRequestEvent)req.getAttribute(API_GATEWAY_EVENT_PROPERTY)).setPath(dispatchTo); return; } if (req.getAttribute(HTTP_API_EVENT_PROPERTY) != null && req.getAttribute(HTTP_API_EVENT_PROPERTY) instanceof APIGatewayV2HTTPEvent) { diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ServletLambdaContainerHandlerBuilder.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ServletLambdaContainerHandlerBuilder.java index fe44ccfc4..c897ca2d3 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ServletLambdaContainerHandlerBuilder.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ServletLambdaContainerHandlerBuilder.java @@ -14,9 +14,10 @@ import com.amazonaws.serverless.exceptions.ContainerInitializationException; import com.amazonaws.serverless.proxy.*; -import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; @@ -97,7 +98,18 @@ public Builder defaultProxy() { .responseWriter((ResponseWriter) new AwsProxyHttpServletResponseWriter()) .securityContextWriter((SecurityContextWriter) new AwsProxySecurityContextWriter()) .exceptionHandler((ExceptionHandler) new AwsProxyExceptionHandler()) - .requestTypeClass((Class) AwsProxyRequest.class) + .requestTypeClass((Class) APIGatewayProxyRequestEvent.class) + .responseTypeClass((Class) AwsProxyResponse.class); + return self(); + } + + public Builder defaultAlbProxy() { + initializationWrapper(new InitializationWrapper()) + .requestReader((RequestReader) new AwsAlbHttpServletRequestReader()) + .responseWriter((ResponseWriter) new AwsAlbHttpServletResponseWriter()) + .securityContextWriter((SecurityContextWriter) new AwsAlbSecurityContextWriter()) + .exceptionHandler((ExceptionHandler) new AwsAlbExceptionHandler()) + .requestTypeClass((Class) ApplicationLoadBalancerRequestEvent.class) .responseTypeClass((Class) AwsProxyResponse.class); return self(); } diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AlbContext.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AlbContext.java deleted file mode 100644 index 527fc222f..000000000 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AlbContext.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance - * with the License. A copy of the License is located at - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES - * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ -package com.amazonaws.serverless.proxy.model; - -/*** - * Context passed by ALB proxy events - */ -public class AlbContext { - private String targetGroupArn; - - - public String getTargetGroupArn() { - return targetGroupArn; - } - - - public void setTargetGroupArn(String targetGroupArn) { - this.targetGroupArn = targetGroupArn; - } -} diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/ApiGatewayAuthorizerContext.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/ApiGatewayAuthorizerContext.java deleted file mode 100644 index c20019544..000000000 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/ApiGatewayAuthorizerContext.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance - * with the License. A copy of the License is located at - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES - * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ -package com.amazonaws.serverless.proxy.model; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -import java.util.HashMap; -import java.util.Map; - - -/** - * Context object used for custom authorizers and Cognito User Pool authorizers. - * - * Custom authorizers populate the principalId field. All other custom values - * returned by the authorizer are accessible via the getContextValue method. - * - * Cognito User Pool authorizers populate the claims object. - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class ApiGatewayAuthorizerContext { - - //------------------------------------------------------------- - // Variables - Private - //------------------------------------------------------------- - - private Map contextProperties = new HashMap<>(); - private String principalId; - private CognitoAuthorizerClaims claims; - - //------------------------------------------------------------- - // Methods - Public - //------------------------------------------------------------- - - @JsonAnyGetter - public String getContextValue(String key) { - return contextProperties.get(key); - } - - - @JsonAnySetter - public void setContextValue(String key, String value) { - contextProperties.put(key, value); - } - - - //------------------------------------------------------------- - // Methods - Getter/Setter - //------------------------------------------------------------- - - public String getPrincipalId() { - return principalId; - } - - - public void setPrincipalId(String principalId) { - this.principalId = principalId; - } - - - public CognitoAuthorizerClaims getClaims() { - return claims; - } - - - public void setClaims(CognitoAuthorizerClaims claims) { - this.claims = claims; - } -} diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/ApiGatewayRequestIdentity.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/ApiGatewayRequestIdentity.java deleted file mode 100644 index 89b1ea2a4..000000000 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/ApiGatewayRequestIdentity.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance - * with the License. A copy of the License is located at - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES - * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ -package com.amazonaws.serverless.proxy.model; - - -import com.amazonaws.serverless.proxy.RequestReader; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - - -/** - * Identity model for the API Gateway request context. This is used in the default AwsProxyRequest object. Contains - * all of the properties declared in the $context.identity API Gateway object so could be re-used for other implementations - * - * @see AwsProxyRequest - * @see RequestReader - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class ApiGatewayRequestIdentity { - - //------------------------------------------------------------- - // Variables - Private - //------------------------------------------------------------- - - private String apiKey; - private String apiKeyId; - private String userArn; - private String cognitoAuthenticationType; - private String caller; - private String userAgent; - private String user; - private String cognitoIdentityPoolId; - private String cognitoIdentityId; - private String cognitoAuthenticationProvider; - private String sourceIp; - private String accountId; - private String accessKey; - - - //------------------------------------------------------------- - // Methods - Getter/Setter - //------------------------------------------------------------- - - public String getApiKey() { - return apiKey; - } - - - public void setApiKey(String apiKey) { - this.apiKey = apiKey; - } - - - public String getApiKeyId() { - return apiKeyId; - } - - - public void setApiKeyId(String apiKeyId) { - this.apiKeyId = apiKeyId; - } - - - public String getUserArn() { - return userArn; - } - - - public void setUserArn(String userArn) { - this.userArn = userArn; - } - - - public String getCognitoAuthenticationType() { - return cognitoAuthenticationType; - } - - - public void setCognitoAuthenticationType(String cognitoAuthenticationType) { - this.cognitoAuthenticationType = cognitoAuthenticationType; - } - - - public String getCaller() { - return caller; - } - - - public void setCaller(String caller) { - this.caller = caller; - } - - - public String getUserAgent() { - return userAgent; - } - - - public void setUserAgent(String userAgent) { - this.userAgent = userAgent; - } - - - public String getUser() { - return user; - } - - - public void setUser(String user) { - this.user = user; - } - - - public String getCognitoIdentityPoolId() { - return cognitoIdentityPoolId; - } - - - public void setCognitoIdentityPoolId(String cognitoIdentityPoolId) { - this.cognitoIdentityPoolId = cognitoIdentityPoolId; - } - - - public String getCognitoIdentityId() { - return cognitoIdentityId; - } - - - public void setCognitoIdentityId(String cognitoIdentityId) { - this.cognitoIdentityId = cognitoIdentityId; - } - - - public String getCognitoAuthenticationProvider() { - return cognitoAuthenticationProvider; - } - - - public void setCognitoAuthenticationProvider(String cognitoAuthenticationProvider) { - this.cognitoAuthenticationProvider = cognitoAuthenticationProvider; - } - - - public String getSourceIp() { - return sourceIp; - } - - - public void setSourceIp(String sourceIp) { - this.sourceIp = sourceIp; - } - - - public String getAccountId() { - return accountId; - } - - - public void setAccountId(String accountId) { - this.accountId = accountId; - } - - - public String getAccessKey() { - return accessKey; - } - - - public void setAccessKey(String accessKey) { - this.accessKey = accessKey; - } -} diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AwsProxyRequest.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AwsProxyRequest.java deleted file mode 100644 index 53ad758f1..000000000 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AwsProxyRequest.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance - * with the License. A copy of the License is located at - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES - * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ -package com.amazonaws.serverless.proxy.model; - -import java.util.HashMap; -import java.util.Map; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Default implementation of the request object from an API Gateway AWS_PROXY integration - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class AwsProxyRequest { - - //------------------------------------------------------------- - // Variables - Private - //------------------------------------------------------------- - - private String body; - private String version; - private String resource; - private AwsProxyRequestContext requestContext; - private MultiValuedTreeMap multiValueQueryStringParameters; - private Map queryStringParameters; - private Headers multiValueHeaders; - private SingleValueHeaders headers; - private Map pathParameters; - private String httpMethod; - private Map stageVariables; - private String path; - private boolean isBase64Encoded; - - public AwsProxyRequest() { - multiValueHeaders = new Headers(); - multiValueQueryStringParameters = new MultiValuedTreeMap<>(); - pathParameters = new HashMap<>(); - stageVariables = new HashMap<>(); - } - - - //------------------------------------------------------------- - // Methods - Getter/Setter - //------------------------------------------------------------- - - @JsonIgnore - public String getQueryString() { - StringBuilder params = new StringBuilder(""); - - if (this.getMultiValueQueryStringParameters() == null) { - return ""; - } - - for (String key : this.getMultiValueQueryStringParameters().keySet()) { - for (String val : this.getMultiValueQueryStringParameters().get(key)) { - String separator = params.length() == 0 ? "?" : "&"; - - params.append(separator + key + "=" + val); - } - } - - return params.toString(); - } - - public RequestSource getRequestSource() { - if (getRequestContext() != null && getRequestContext().getElb() != null) { - return RequestSource.ALB; - } - - return RequestSource.API_GATEWAY; - } - - - public String getBody() { - return body; - } - - - public void setBody(String body) { - this.body = body; - } - - - public String getResource() { - return resource; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public void setResource(String resource) { - this.resource = resource; - } - - - public AwsProxyRequestContext getRequestContext() { - return requestContext; - } - - - public void setRequestContext(AwsProxyRequestContext requestContext) { - this.requestContext = requestContext; - } - - public MultiValuedTreeMap getMultiValueQueryStringParameters() { - return multiValueQueryStringParameters; - } - - public void setMultiValueQueryStringParameters( - MultiValuedTreeMap multiValueQueryStringParameters) { - this.multiValueQueryStringParameters = multiValueQueryStringParameters; - } - - public Map getQueryStringParameters() { - return queryStringParameters; - } - - public void setQueryStringParameters(Map queryStringParameters) { - this.queryStringParameters = queryStringParameters; - } - - public Headers getMultiValueHeaders() { - return multiValueHeaders; - } - - public void setMultiValueHeaders(Headers multiValueHeaders) { - this.multiValueHeaders = multiValueHeaders; - } - - public SingleValueHeaders getHeaders() { - return headers; - } - - public void setHeaders(SingleValueHeaders headers) { - this.headers = headers; - } - - - public Map getPathParameters() { - return pathParameters; - } - - - public void setPathParameters(Map pathParameters) { - this.pathParameters = pathParameters; - } - - - public String getHttpMethod() { - return httpMethod; - } - - - public void setHttpMethod(String httpMethod) { - this.httpMethod = httpMethod; - } - - - public Map getStageVariables() { - return stageVariables; - } - - - public void setStageVariables(Map stageVariables) { - this.stageVariables = stageVariables; - } - - - public String getPath() { - return path; - } - - - public void setPath(String path) { - this.path = path; - } - - @JsonProperty("isBase64Encoded") - public boolean isBase64Encoded() { - return isBase64Encoded; - } - - - public void setIsBase64Encoded(boolean base64Encoded) { - isBase64Encoded = base64Encoded; - } -} diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AwsProxyRequestContext.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AwsProxyRequestContext.java deleted file mode 100644 index e7cb1bf61..000000000 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AwsProxyRequestContext.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance - * with the License. A copy of the License is located at - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES - * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ -package com.amazonaws.serverless.proxy.model; - - -import com.amazonaws.serverless.proxy.RequestReader; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - - -/** - * The API Gateway request context object. This is used by the default implementation of the AWS_PROXY integration type. - * All of the values are part of the API Gateway $context variable so this object could be reused with custom request - * readers. - * - * @see AwsProxyRequest - * @see RequestReader - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class AwsProxyRequestContext { - - //------------------------------------------------------------- - // Variables - Private - //------------------------------------------------------------- - - private String resourceId; - private String apiId; - private String resourcePath; - private String httpMethod; - private String requestId; - private String extendedRequestId; - private String accountId; - private ApiGatewayRequestIdentity identity; - private ApiGatewayAuthorizerContext authorizer; - private String stage; - private String path; - private String protocol; - private String requestTime; - private long requestTimeEpoch; - - private AlbContext elb; - - - //------------------------------------------------------------- - // Methods - Getter/Setter - //------------------------------------------------------------- - - public String getResourceId() { - return resourceId; - } - - - public void setResourceId(String resourceId) { - this.resourceId = resourceId; - } - - - public String getApiId() { - return apiId; - } - - - public void setApiId(String apiId) { - this.apiId = apiId; - } - - - public String getResourcePath() { - return resourcePath; - } - - - public void setResourcePath(String resourcePath) { - this.resourcePath = resourcePath; - } - - - public String getHttpMethod() { - return httpMethod; - } - - - public void setHttpMethod(String httpMethod) { - this.httpMethod = httpMethod; - } - - - public String getRequestId() { - return requestId; - } - - - public void setRequestId(String requestId) { - this.requestId = requestId; - } - - - public String getExtendedRequestId() { - return extendedRequestId; - } - - - public void setExtendedRequestId(String extendedRequestId) { - this.extendedRequestId = extendedRequestId; - } - - - public String getAccountId() { - return accountId; - } - - - public void setAccountId(String accountId) { - this.accountId = accountId; - } - - - public ApiGatewayRequestIdentity getIdentity() { - return identity; - } - - - public void setIdentity(ApiGatewayRequestIdentity identity) { - this.identity = identity; - } - - - public String getStage() { - return stage; - } - - - public void setStage(String stage) { - this.stage = stage; - } - - public String getPath() { - return path; - } - - - public void setPath(String path) { - this.path = path; - } - - - public ApiGatewayAuthorizerContext getAuthorizer() { - return authorizer; - } - - - public void setAuthorizer(ApiGatewayAuthorizerContext authorizer) { - this.authorizer = authorizer; - } - - - public String getProtocol() { - return protocol; - } - - - public void setProtocol(String protocol) { - this.protocol = protocol; - } - - - public String getRequestTime() { - return requestTime; - } - - - public void setRequestTime(String requestTime) { - this.requestTime = requestTime; - } - - - public long getRequestTimeEpoch() { - return requestTimeEpoch; - } - - - public void setRequestTimeEpoch(long requestTimeEpoch) { - this.requestTimeEpoch = requestTimeEpoch; - } - - - public AlbContext getElb() { - return elb; - } - - - public void setElb(AlbContext elb) { - this.elb = elb; - } -} diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AwsProxyResponse.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AwsProxyResponse.java index 5f70d2a49..a23503c70 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AwsProxyResponse.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AwsProxyResponse.java @@ -13,9 +13,11 @@ package com.amazonaws.serverless.proxy.model; +import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; import java.util.Map; @@ -32,7 +34,7 @@ public class AwsProxyResponse { private int statusCode; private String statusDescription; private Map headers; - private Headers multiValueHeaders; + private Map> multiValueHeaders; private String body; private boolean isBase64Encoded; @@ -71,8 +73,8 @@ public void addHeader(String key, String value) { if (this.multiValueHeaders == null) { this.multiValueHeaders = new Headers(); } - - this.multiValueHeaders.add(key, value); + AwsHttpServletRequestHelper.putSingle(this.multiValueHeaders, key, value); + //this.multiValueHeaders.add(key, value); } @@ -100,12 +102,12 @@ public void setHeaders(Map headers) { } - public Headers getMultiValueHeaders() { + public Map> getMultiValueHeaders() { return multiValueHeaders; } - public void setMultiValueHeaders(Headers multiValueHeaders) { + public void setMultiValueHeaders(Map> multiValueHeaders) { this.multiValueHeaders = multiValueHeaders; } diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/CognitoAuthorizerClaims.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/CognitoAuthorizerClaims.java deleted file mode 100644 index 34e2e47ab..000000000 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/CognitoAuthorizerClaims.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance - * with the License. A copy of the License is located at - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES - * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ -package com.amazonaws.serverless.proxy.model; - - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.HashMap; -import java.util.Map; - - -/** - * This object represents the claims property in the authorizer context of a request. The claims object is normally populated - * by a Cognito User Pool authorizer and contains the following fields: - *
- * "claims": {
- *     "sub": "42df3b02-29f1-4779-a3e5-eff92ff280b2",
- *     "aud": "2k3no2j1rjjbqaskc4bk0ub29b",
- *     "email_verified": "true",
- *     "token_use": "id",
- *     "auth_time": "1492467169",
- *     "iss": "https://cognito-idp.us-east-2.amazonaws.com/us-east-2_Adx5ZHePg",
- *     "cognito:username": "sapessi",
- *     "expiration": "Mon Apr 17 23:12:49 UTC 2017",
- *     "issuedAt": "Mon Apr 17 22:12:49 UTC 2017",
- *     "email": "bulianis@amazon.com"
- * }
- * 
- */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class CognitoAuthorizerClaims { - - //------------------------------------------------------------- - // Variables - Private - //------------------------------------------------------------- - - private Map claims = new HashMap<>(); - - @JsonProperty(value = "sub") - private String subject; - @JsonProperty(value = "aud") - private String audience; - @JsonProperty(value = "iss") - private String issuer; - @JsonProperty(value = "token_use") - private String tokenUse; - @JsonProperty(value = "cognito:username") - private String username; - private String email; - @JsonProperty(value = "email_verified") - private boolean emailVerified; - @JsonProperty(value = "auth_time") - private Long authTime; - @JsonProperty(value = "exp") - private String expiration; - @JsonProperty(value = "iat") - private String issuedAt; - - - //------------------------------------------------------------- - // Methods - Getter/Setter - //------------------------------------------------------------- - - @JsonAnyGetter - public String getClaim(String claim) { - return claims.get(claim); - } - - @JsonAnySetter - public void setClaim(String claim, String value) { - claims.put(claim, value); - } - - public String getSubject() { return this.subject; } - - - public void setSubject(String subject) { - this.subject = subject; - } - - - public String getAudience() { - return audience; - } - - - public void setAudience(String audience) { - this.audience = audience; - } - - - public String getIssuer() { - return issuer; - } - - - public void setIssuer(String issuer) { - this.issuer = issuer; - } - - - public String getTokenUse() { - return tokenUse; - } - - - public void setTokenUse(String tokenUse) { - this.tokenUse = tokenUse; - } - - - public String getUsername() { - return username; - } - - - public void setUsername(String username) { - this.username = username; - } - - - public String getEmail() { - return email; - } - - - public void setEmail(String email) { - this.email = email; - } - - - public boolean isEmailVerified() { - return emailVerified; - } - - - public void setEmailVerified(boolean emailVerified) { - this.emailVerified = emailVerified; - } - - - public Long getAuthTime() { - return authTime; - } - - - public void setAuthTime(Long authTime) { - this.authTime = authTime; - } - - - public String getExpiration() { - return expiration; - } - - - public void setExpiration(String expiration) { - this.expiration = expiration; - } - - - public String getIssuedAt() { - return issuedAt; - } - - - public void setIssuedAt(String issuedAt) { - this.issuedAt = issuedAt; - } -} diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/RequestSource.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/RequestSource.java deleted file mode 100644 index c819fdcc0..000000000 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/RequestSource.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance - * with the License. A copy of the License is located at - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES - * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ -package com.amazonaws.serverless.proxy.model; - -public enum RequestSource { - ALB, - API_GATEWAY -} \ No newline at end of file diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/SingleValueHeaders.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/SingleValueHeaders.java deleted file mode 100644 index 6dbf03936..000000000 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/SingleValueHeaders.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance - * with the License. A copy of the License is located at - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES - * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ -package com.amazonaws.serverless.proxy.model; - -import java.util.TreeMap; - -public class SingleValueHeaders extends TreeMap { - - private static final long serialVersionUID = 42L; - - public SingleValueHeaders() { - super(String.CASE_INSENSITIVE_ORDER); - } -} diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsAlbExceptionHandlerTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsAlbExceptionHandlerTest.java new file mode 100644 index 000000000..4c48228fc --- /dev/null +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsAlbExceptionHandlerTest.java @@ -0,0 +1,259 @@ +package com.amazonaws.serverless.proxy; + +import com.amazonaws.serverless.exceptions.InvalidRequestEventException; +import com.amazonaws.serverless.exceptions.InvalidResponseObjectException; +import com.amazonaws.serverless.proxy.model.AwsProxyResponse; +import com.amazonaws.serverless.proxy.model.ErrorModel; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.ws.rs.InternalServerErrorException; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class AwsAlbExceptionHandlerTest { + private static final String INTERNAL_SERVER_ERROR_MESSAGE = "Internal server error"; + private static final String INVALID_REQUEST_MESSAGE = "Invalid request error"; + private static final String INVALID_RESPONSE_MESSAGE = "Invalid response error"; + private AwsAlbExceptionHandler exceptionHandler; + private ObjectMapper objectMapper; + + + @BeforeEach + public void setUp() { + exceptionHandler = new AwsAlbExceptionHandler(); + objectMapper = new ObjectMapper(); + } + + @Test + void typedHandle_InvalidRequestEventException_500State() { + AwsProxyResponse resp = exceptionHandler.handle(new InvalidRequestEventException(INVALID_REQUEST_MESSAGE, null)); + + assertNotNull(resp); + assertEquals(500, resp.getStatusCode()); + } + + @Test + void typedHandle_InvalidRequestEventException_responseString() + throws JsonProcessingException { + AwsProxyResponse resp = exceptionHandler.handle(new InvalidRequestEventException(INVALID_REQUEST_MESSAGE, null)); + + assertNotNull(resp); + String body = objectMapper.writeValueAsString(new ErrorModel(AwsProxyExceptionHandler.INTERNAL_SERVER_ERROR)); + assertEquals(body, resp.getBody()); + } + + @Test + void typedHandle_InvalidRequestEventException_jsonContentTypeHeader() { + AwsProxyResponse resp = exceptionHandler.handle(new InvalidRequestEventException(INVALID_REQUEST_MESSAGE, null)); + + assertNotNull(resp); + assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE)); + assertEquals(MediaType.APPLICATION_JSON, resp.getMultiValueHeaders().get(HttpHeaders.CONTENT_TYPE).get(0)); + } + + @Test + void typedHandle_InvalidResponseObjectException_502State() { + AwsProxyResponse resp = exceptionHandler.handle(new InvalidResponseObjectException(INVALID_RESPONSE_MESSAGE, null)); + + assertNotNull(resp); + assertEquals(502, resp.getStatusCode()); + } + + @Test + void typedHandle_InvalidResponseObjectException_responseString() + throws JsonProcessingException { + AwsProxyResponse resp = exceptionHandler.handle(new InvalidResponseObjectException(INVALID_RESPONSE_MESSAGE, null)); + + assertNotNull(resp); + String body = objectMapper.writeValueAsString(new ErrorModel(AwsProxyExceptionHandler.GATEWAY_TIMEOUT_ERROR)); + assertEquals(body, resp.getBody()); + } + + @Test + void typedHandle_InvalidResponseObjectException_jsonContentTypeHeader() { + AwsProxyResponse resp = exceptionHandler.handle(new InvalidResponseObjectException(INVALID_RESPONSE_MESSAGE, null)); + + assertNotNull(resp); + assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE)); + assertEquals(MediaType.APPLICATION_JSON, resp.getMultiValueHeaders().get(HttpHeaders.CONTENT_TYPE).get(0)); + } + + @Test + void typedHandle_InternalServerErrorException_500State() { + // Needed to mock InternalServerErrorException because it leverages RuntimeDelegate to set an internal + // response object. + InternalServerErrorException mockInternalServerErrorException = Mockito.mock(InternalServerErrorException.class); + Mockito.when(mockInternalServerErrorException.getMessage()).thenReturn(INTERNAL_SERVER_ERROR_MESSAGE); + + AwsProxyResponse resp = exceptionHandler.handle(mockInternalServerErrorException); + + assertNotNull(resp); + assertEquals(500, resp.getStatusCode()); + } + + @Test + void typedHandle_InternalServerErrorException_responseString() + throws JsonProcessingException { + InternalServerErrorException mockInternalServerErrorException = Mockito.mock(InternalServerErrorException.class); + Mockito.when(mockInternalServerErrorException.getMessage()).thenReturn(INTERNAL_SERVER_ERROR_MESSAGE); + + AwsProxyResponse resp = exceptionHandler.handle(mockInternalServerErrorException); + + assertNotNull(resp); + String body = objectMapper.writeValueAsString(new ErrorModel(AwsProxyExceptionHandler.INTERNAL_SERVER_ERROR)); + assertEquals(body, resp.getBody()); + } + + @Test + void typedHandle_InternalServerErrorException_jsonContentTypeHeader() { + InternalServerErrorException mockInternalServerErrorException = Mockito.mock(InternalServerErrorException.class); + Mockito.when(mockInternalServerErrorException.getMessage()).thenReturn(INTERNAL_SERVER_ERROR_MESSAGE); + + AwsProxyResponse resp = exceptionHandler.handle(mockInternalServerErrorException); + + assertNotNull(resp); + assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE)); + assertEquals(MediaType.APPLICATION_JSON, resp.getMultiValueHeaders().get(HttpHeaders.CONTENT_TYPE).get(0)); + } + + @Test + void typedHandle_NullPointerException_responseObject() + throws JsonProcessingException { + AwsProxyResponse resp = exceptionHandler.handle(new NullPointerException()); + + assertNotNull(resp); + assertEquals(502, resp.getStatusCode()); + assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE)); + assertEquals(MediaType.APPLICATION_JSON, resp.getMultiValueHeaders().get(HttpHeaders.CONTENT_TYPE).get(0)); + String body = objectMapper.writeValueAsString(new ErrorModel(AwsProxyExceptionHandler.GATEWAY_TIMEOUT_ERROR)); + assertEquals(body, resp.getBody()); + } + + @Test + void streamHandle_InvalidRequestEventException_500State() + throws IOException { + ByteArrayOutputStream respStream = new ByteArrayOutputStream(); + exceptionHandler.handle(new InvalidRequestEventException(INVALID_REQUEST_MESSAGE, null), respStream); + + assertNotNull(respStream); + assertTrue(respStream.size() > 0); + AwsProxyResponse resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponse.class); + assertNotNull(resp); + assertEquals(500, resp.getStatusCode()); + } + + @Test + void streamHandle_InvalidRequestEventException_responseString() + throws IOException { + ByteArrayOutputStream respStream = new ByteArrayOutputStream(); + exceptionHandler.handle(new InvalidRequestEventException(INVALID_REQUEST_MESSAGE, null), respStream); + + assertNotNull(respStream); + assertTrue(respStream.size() > 0); + AwsProxyResponse resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponse.class); + assertNotNull(resp); + String body = objectMapper.writeValueAsString(new ErrorModel(AwsProxyExceptionHandler.INTERNAL_SERVER_ERROR)); + assertEquals(body, resp.getBody()); + } + + @Test + void streamHandle_InvalidRequestEventException_jsonContentTypeHeader() + throws IOException { + ByteArrayOutputStream respStream = new ByteArrayOutputStream(); + exceptionHandler.handle(new InvalidRequestEventException(INVALID_REQUEST_MESSAGE, null), respStream); + + assertNotNull(respStream); + assertTrue(respStream.size() > 0); + AwsProxyResponse resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponse.class); + assertNotNull(resp); + assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE)); + assertEquals(MediaType.APPLICATION_JSON, resp.getMultiValueHeaders().get(HttpHeaders.CONTENT_TYPE).get(0)); + } + + @Test + void streamHandle_InvalidResponseObjectException_502State() + throws IOException { + ByteArrayOutputStream respStream = new ByteArrayOutputStream(); + exceptionHandler.handle(new InvalidResponseObjectException(INVALID_RESPONSE_MESSAGE, null), respStream); + + assertNotNull(respStream); + assertTrue(respStream.size() > 0); + AwsProxyResponse resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponse.class); + assertNotNull(resp); + assertEquals(502, resp.getStatusCode()); + } + + @Test + void streamHandle_InvalidResponseObjectException_responseString() + throws IOException { + ByteArrayOutputStream respStream = new ByteArrayOutputStream(); + exceptionHandler.handle(new InvalidResponseObjectException(INVALID_RESPONSE_MESSAGE, null), respStream); + + assertNotNull(respStream); + assertTrue(respStream.size() > 0); + AwsProxyResponse resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponse.class); + assertNotNull(resp); + String body = objectMapper.writeValueAsString(new ErrorModel(AwsProxyExceptionHandler.GATEWAY_TIMEOUT_ERROR)); + assertEquals(body, resp.getBody()); + } + + @Test + void streamHandle_InvalidResponseObjectException_jsonContentTypeHeader() + throws IOException { + ByteArrayOutputStream respStream = new ByteArrayOutputStream(); + exceptionHandler.handle(new InvalidResponseObjectException(INVALID_RESPONSE_MESSAGE, null), respStream); + + assertNotNull(respStream); + assertTrue(respStream.size() > 0); + AwsProxyResponse resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponse.class); + assertNotNull(resp); + assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE)); + assertEquals(MediaType.APPLICATION_JSON, resp.getMultiValueHeaders().get(HttpHeaders.CONTENT_TYPE).get(0)); + } + + @Test + void errorMessage_InternalServerError_staticString() { + assertEquals("Internal Server Error", AwsProxyExceptionHandler.INTERNAL_SERVER_ERROR); + } + + @Test + void errorMessage_GatewayTimeout_staticString() { + assertEquals("Gateway timeout", AwsProxyExceptionHandler.GATEWAY_TIMEOUT_ERROR); + } + + @Test + void getErrorJson_ErrorModel_validJson() + throws IOException { + String output = exceptionHandler.getErrorJson(INVALID_RESPONSE_MESSAGE); + assertNotNull(output); + ErrorModel error = objectMapper.readValue(output, ErrorModel.class); + assertNotNull(error); + assertEquals(INVALID_RESPONSE_MESSAGE, error.getMessage()); + } + + @Test + void getErrorJson_JsonParsinException_validJson() + throws IOException { + ObjectMapper mockMapper = mock(ObjectMapper.class); + JsonProcessingException exception = mock(JsonProcessingException.class); + when(mockMapper.writeValueAsString(any(Object.class))).thenThrow(exception); + + String output = exceptionHandler.getErrorJson(INVALID_RESPONSE_MESSAGE); + assertNotNull(output); + ErrorModel error = objectMapper.readValue(output, ErrorModel.class); + assertNotNull(error); + assertEquals(INVALID_RESPONSE_MESSAGE, error.getMessage()); + } +} diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsProxyExceptionHandlerTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsProxyExceptionHandlerTest.java index f94371d69..c6083b59b 100644 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsProxyExceptionHandlerTest.java +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsProxyExceptionHandlerTest.java @@ -3,6 +3,7 @@ import com.amazonaws.serverless.exceptions.InvalidRequestEventException; import com.amazonaws.serverless.exceptions.InvalidResponseObjectException; +import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; import com.amazonaws.serverless.proxy.model.ErrorModel; import com.fasterxml.jackson.core.JsonProcessingException; @@ -61,7 +62,7 @@ void typedHandle_InvalidRequestEventException_jsonContentTypeHeader() { assertNotNull(resp); assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE)); - assertEquals(MediaType.APPLICATION_JSON, resp.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE)); + assertEquals(MediaType.APPLICATION_JSON, AwsHttpServletRequestHelper.getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE)); //resp.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE)); } @Test @@ -88,7 +89,7 @@ void typedHandle_InvalidResponseObjectException_jsonContentTypeHeader() { assertNotNull(resp); assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE)); - assertEquals(MediaType.APPLICATION_JSON, resp.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE)); + assertEquals(MediaType.APPLICATION_JSON, AwsHttpServletRequestHelper.getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE)); } @Test @@ -126,7 +127,7 @@ void typedHandle_InternalServerErrorException_jsonContentTypeHeader() { assertNotNull(resp); assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE)); - assertEquals(MediaType.APPLICATION_JSON, resp.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE)); + assertEquals(MediaType.APPLICATION_JSON, AwsHttpServletRequestHelper.getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE)); } @Test @@ -137,7 +138,7 @@ void typedHandle_NullPointerException_responseObject() assertNotNull(resp); assertEquals(502, resp.getStatusCode()); assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE)); - assertEquals(MediaType.APPLICATION_JSON, resp.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE)); + assertEquals(MediaType.APPLICATION_JSON, AwsHttpServletRequestHelper.getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE)); String body = objectMapper.writeValueAsString(new ErrorModel(AwsProxyExceptionHandler.GATEWAY_TIMEOUT_ERROR)); assertEquals(body, resp.getBody()); } @@ -180,7 +181,7 @@ void streamHandle_InvalidRequestEventException_jsonContentTypeHeader() AwsProxyResponse resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponse.class); assertNotNull(resp); assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE)); - assertEquals(MediaType.APPLICATION_JSON, resp.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE)); + assertEquals(MediaType.APPLICATION_JSON, AwsHttpServletRequestHelper.getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE)); } @Test @@ -221,7 +222,7 @@ void streamHandle_InvalidResponseObjectException_jsonContentTypeHeader() AwsProxyResponse resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponse.class); assertNotNull(resp); assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE)); - assertEquals(MediaType.APPLICATION_JSON, resp.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE)); + assertEquals(MediaType.APPLICATION_JSON, AwsHttpServletRequestHelper.getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE)); } @Test diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsProxySecurityContextWriterTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsProxySecurityContextWriterTest.java index 8a2134f48..332d4442c 100644 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsProxySecurityContextWriterTest.java +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsProxySecurityContextWriterTest.java @@ -1,8 +1,8 @@ package com.amazonaws.serverless.proxy; -import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -23,13 +23,13 @@ public void setUp() { @Test void write_returnClass_securityContext() throws NoSuchMethodException { - Method writeMethod = writer.getClass().getMethod("writeSecurityContext", AwsProxyRequest.class, Context.class); + Method writeMethod = writer.getClass().getMethod("writeSecurityContext", APIGatewayProxyRequestEvent.class, Context.class); assertEquals(SecurityContext.class, writeMethod.getReturnType()); } @Test void write_noAuth_emptySecurityContext() { - AwsProxyRequest request = new AwsProxyRequestBuilder("/test").build(); + APIGatewayProxyRequestEvent request = new AwsProxyRequestBuilder("/test").build(); SecurityContext context = writer.writeSecurityContext(request, null); assertNotNull(context); diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandlerTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandlerTest.java index d6af5112e..9a1ecf077 100644 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandlerTest.java +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandlerTest.java @@ -7,9 +7,9 @@ import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletResponseWriter; import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext; -import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import org.apache.hc.client5.http.impl.classic.RequestAbortedException; import org.junit.jupiter.api.Test; @@ -23,7 +23,7 @@ public class LambdaContainerHandlerTest { private boolean throwException = false; ExceptionContainerHandlerTest handler = new ExceptionContainerHandlerTest( - AwsProxyRequest.class, AwsProxyResponse.class, + APIGatewayProxyRequestEvent.class, AwsProxyResponse.class, new AwsProxyHttpServletRequestReader(), new AwsProxyHttpServletResponseWriter(), new AwsProxySecurityContextWriter(), new AwsProxyExceptionHandler(), new InitializationWrapper() ); @@ -69,12 +69,12 @@ void noException_returnsResponse() { assertEquals("OK", resp.getBody()); } - public class ExceptionContainerHandlerTest extends LambdaContainerHandler { + public class ExceptionContainerHandlerTest extends LambdaContainerHandler { public static final String RUNTIME_MESSAGE = "test RuntimeException"; public static final String NON_RUNTIME_MESSAGE = "test NonRuntimeException"; - protected ExceptionContainerHandlerTest(Class requestClass, Class responseClass, RequestReader requestReader, ResponseWriter responseWriter, SecurityContextWriter securityContextWriter, ExceptionHandler exceptionHandler, InitializationWrapper init) { + protected ExceptionContainerHandlerTest(Class requestClass, Class responseClass, RequestReader requestReader, ResponseWriter responseWriter, SecurityContextWriter securityContextWriter, ExceptionHandler exceptionHandler, InitializationWrapper init) { super(requestClass, responseClass, requestReader, responseWriter, securityContextWriter, exceptionHandler, init); } diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/jaxrs/AwsProxySecurityContextTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/jaxrs/AwsProxySecurityContextTest.java index ed89bf86c..679b4e8bf 100644 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/jaxrs/AwsProxySecurityContextTest.java +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/jaxrs/AwsProxySecurityContextTest.java @@ -1,30 +1,21 @@ package com.amazonaws.serverless.proxy.internal.jaxrs; -import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import org.junit.jupiter.api.Test; import java.security.Principal; -import static com.amazonaws.serverless.proxy.internal.jaxrs.AwsProxySecurityContext.ALB_ACESS_TOKEN_HEADER; -import static com.amazonaws.serverless.proxy.internal.jaxrs.AwsProxySecurityContext.ALB_IDENTITY_HEADER; import static com.amazonaws.serverless.proxy.internal.jaxrs.AwsProxySecurityContext.AUTH_SCHEME_COGNITO_POOL; -import static com.amazonaws.serverless.proxy.internal.jaxrs.AwsProxySecurityContext.AUTH_SCHEME_CUSTOM; import static org.junit.jupiter.api.Assertions.*; public class AwsProxySecurityContextTest { private static final String CLAIM_KEY = "custom:claim"; private static final String CLAIM_VALUE = "customClaimant"; private static final String COGNITO_IDENTITY_ID = "us-east-2:123123123123"; - private static final AwsProxyRequest REQUEST_NO_AUTH = new AwsProxyRequestBuilder("/hello", "GET").build(); - private static final AwsProxyRequest ALB_REQUEST_NO_AUTH = new AwsProxyRequestBuilder("/hello", "GET").alb().build(); - private static final AwsProxyRequest REQUEST_COGNITO_USER_POOL = new AwsProxyRequestBuilder("/hello", "GET") + private static final APIGatewayProxyRequestEvent REQUEST_NO_AUTH = new AwsProxyRequestBuilder("/hello", "GET").build(); + private static final APIGatewayProxyRequestEvent REQUEST_COGNITO_USER_POOL = new AwsProxyRequestBuilder("/hello", "GET") .cognitoUserPool(COGNITO_IDENTITY_ID).claim(CLAIM_KEY, CLAIM_VALUE).build(); - private static final AwsProxyRequest ALB_REQUEST_COGNITO_USER_POOL = new AwsProxyRequestBuilder("/hello", "GET") - .alb() - .header(ALB_ACESS_TOKEN_HEADER, "xxxxx") - .header(ALB_IDENTITY_HEADER, COGNITO_IDENTITY_ID) - .build(); @Test void localVars_constructor_nullValues() { @@ -40,15 +31,6 @@ void localVars_constructor_ValidRequest() { assertNull(context.getLambdaContext()); } - @Test - void alb_noAuth_expectEmptyScheme() { - AwsProxySecurityContext context = new AwsProxySecurityContext(null, ALB_REQUEST_NO_AUTH); - assertEquals(ALB_REQUEST_NO_AUTH, context.getEvent()); - assertNull(context.getLambdaContext()); - assertFalse(context.isSecure()); - assertNull(context.getAuthenticationScheme()); - } - @Test void authScheme_getAuthenticationScheme_userPool() { AwsProxySecurityContext context = new AwsProxySecurityContext(null, REQUEST_COGNITO_USER_POOL); @@ -63,14 +45,6 @@ void authScheme_getPrincipal_userPool() { assertEquals(COGNITO_IDENTITY_ID, context.getUserPrincipal().getName()); } - @Test - void alb_cognitoAuth_expectCustomSchemeAndCorrectPrincipal() { - AwsProxySecurityContext context = new AwsProxySecurityContext(null, ALB_REQUEST_COGNITO_USER_POOL); - assertTrue(context.isSecure()); - assertEquals(AUTH_SCHEME_CUSTOM, context.getAuthenticationScheme()); - assertEquals(COGNITO_IDENTITY_ID, context.getUserPrincipal().getName()); - } - @Test void userPool_getClaims_retrieveCustomClaim() { AwsProxySecurityContext context = new AwsProxySecurityContext(null, REQUEST_COGNITO_USER_POOL); @@ -79,7 +53,7 @@ void userPool_getClaims_retrieveCustomClaim() { assertEquals(COGNITO_IDENTITY_ID, userPrincipal.getName()); assertTrue(userPrincipal instanceof AwsProxySecurityContext.CognitoUserPoolPrincipal); - assertNotNull(((AwsProxySecurityContext.CognitoUserPoolPrincipal)userPrincipal).getClaims().getClaim(CLAIM_KEY)); - assertEquals(CLAIM_VALUE, ((AwsProxySecurityContext.CognitoUserPoolPrincipal)userPrincipal).getClaims().getClaim(CLAIM_KEY)); + assertNotNull(((AwsProxySecurityContext.CognitoUserPoolPrincipal)userPrincipal).getClaims().get(CLAIM_KEY)); + assertEquals(CLAIM_VALUE, ((AwsProxySecurityContext.CognitoUserPoolPrincipal)userPrincipal).getClaims().get(CLAIM_KEY)); } } diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/ApacheCombinedServletLogFormatterTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/ApacheCombinedServletLogFormatterTest.java index 69b2d7985..3873073f2 100644 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/ApacheCombinedServletLogFormatterTest.java +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/ApacheCombinedServletLogFormatterTest.java @@ -1,9 +1,7 @@ package com.amazonaws.serverless.proxy.internal.servlet; -import com.amazonaws.serverless.proxy.model.ApiGatewayRequestIdentity; -import com.amazonaws.serverless.proxy.model.AwsProxyRequest; -import com.amazonaws.serverless.proxy.model.AwsProxyRequestContext; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -27,16 +25,16 @@ public class ApacheCombinedServletLogFormatterTest { private HttpServletRequest mockServletRequest; private HttpServletResponse mockServletResponse; - private AwsProxyRequest proxyRequest; - private AwsProxyRequestContext context; + private APIGatewayProxyRequestEvent proxyRequest; + private APIGatewayProxyRequestEvent.ProxyRequestContext context; @BeforeEach public void setup() { - proxyRequest = new AwsProxyRequest(); + proxyRequest = new APIGatewayProxyRequestEvent(); Clock fixedClock = Clock.fixed(Instant.ofEpochSecond(665888523L), ZoneId.of("UTC")); mockServletRequest = mock(HttpServletRequest.class); - context = new AwsProxyRequestContext(); - context.setIdentity(new ApiGatewayRequestIdentity()); + context = new APIGatewayProxyRequestEvent.ProxyRequestContext(); + context.setIdentity(new APIGatewayProxyRequestEvent.RequestIdentity()); when(mockServletRequest.getAttribute(eq(API_GATEWAY_CONTEXT_PROPERTY))) .thenReturn(context); when(mockServletRequest.getMethod()) @@ -62,7 +60,7 @@ void logsCurrentTimeWhenContextNull() { @Test void logsCurrentTimeWhenRequestTimeZero() { // given - context.setRequestTimeEpoch(0); + context.setRequestTimeEpoch(0L); // when String actual = sut.format(mockServletRequest, mockServletResponse, null); diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletRequestReaderTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletRequestReaderTest.java new file mode 100644 index 000000000..26fc9de2c --- /dev/null +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletRequestReaderTest.java @@ -0,0 +1,161 @@ +package com.amazonaws.serverless.proxy.internal.servlet; + +import com.amazonaws.serverless.exceptions.InvalidRequestEventException; +import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; +import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; +import com.amazonaws.serverless.proxy.model.ContainerConfig; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.HttpHeaders; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class AwsAlbHttpServletRequestReaderTest { + private AwsAlbHttpServletRequestReader reader = new AwsAlbHttpServletRequestReader(); + + private static final String TEST_HEADER_KEY = "x-test"; + private static final String TEST_HEADER_VALUE = "header"; + private static final String ENCODED_REQUEST_PATH = "/foo/bar/Some%20Thing"; + private static final String DECODED_REQUEST_PATH = "/foo/bar/Some Thing"; + + @Test + void readRequest_validAwsProxy_populatedRequest() { + ApplicationLoadBalancerRequestEvent request = new AwsProxyRequestBuilder("/path", "GET").header(TEST_HEADER_KEY, TEST_HEADER_VALUE).toAlbRequest(); + try { + HttpServletRequest servletRequest = reader.readRequest(request, null, null, ContainerConfig.defaultConfig()); + assertNotNull(servletRequest.getHeader(TEST_HEADER_KEY)); + assertEquals(TEST_HEADER_VALUE, servletRequest.getHeader(TEST_HEADER_KEY)); + } catch (InvalidRequestEventException e) { + e.printStackTrace(); + fail("Could not read request"); + } + } + + @Test + void readRequest_urlDecode_expectDecodedPath() { + ApplicationLoadBalancerRequestEvent request = new AwsProxyRequestBuilder(ENCODED_REQUEST_PATH, "GET").toAlbRequest(); + try { + HttpServletRequest servletRequest = reader.readRequest(request, null, null, ContainerConfig.defaultConfig()); + assertNotNull(servletRequest); + assertEquals(DECODED_REQUEST_PATH, servletRequest.getPathInfo()); + assertEquals(ENCODED_REQUEST_PATH, servletRequest.getRequestURI()); + } catch (InvalidRequestEventException e) { + e.printStackTrace(); + fail("Could not read request"); + } + + } + + @Test + void readRequest_contentCharset_doesNotOverrideRequestCharset() { + String requestCharset = "application/json; charset=UTF-8"; + ApplicationLoadBalancerRequestEvent request = new AwsProxyRequestBuilder(ENCODED_REQUEST_PATH, "GET").header(HttpHeaders.CONTENT_TYPE, requestCharset).toAlbRequest(); + try { + HttpServletRequest servletRequest = reader.readRequest(request, null, null, ContainerConfig.defaultConfig()); + assertNotNull(servletRequest); + assertNotNull(servletRequest.getHeader(HttpHeaders.CONTENT_TYPE)); + assertEquals(requestCharset, servletRequest.getHeader(HttpHeaders.CONTENT_TYPE)); + assertEquals("UTF-8", servletRequest.getCharacterEncoding()); + } catch (InvalidRequestEventException e) { + e.printStackTrace(); + fail("Could not read request"); + } + } + + @Test + void readRequest_contentCharset_setsDefaultCharsetWhenNotSpecified() { + String requestCharset = "application/json"; + ApplicationLoadBalancerRequestEvent request = new AwsProxyRequestBuilder(ENCODED_REQUEST_PATH, "GET").header(HttpHeaders.CONTENT_TYPE, requestCharset).toAlbRequest(); + try { + HttpServletRequest servletRequest = reader.readRequest(request, null, null, ContainerConfig.defaultConfig()); + assertNotNull(servletRequest); + assertNotNull(servletRequest.getHeader(HttpHeaders.CONTENT_TYPE)); + String contentAndCharset = requestCharset + "; charset=" + LambdaContainerHandler.getContainerConfig().getDefaultContentCharset(); + assertEquals(contentAndCharset, servletRequest.getHeader(HttpHeaders.CONTENT_TYPE)); + assertEquals(LambdaContainerHandler.getContainerConfig().getDefaultContentCharset(), servletRequest.getCharacterEncoding()); + } catch (InvalidRequestEventException e) { + e.printStackTrace(); + fail("Could not read request"); + } + } + + @Test + void readRequest_contentCharset_appendsCharsetToComplextContentType() { + String contentType = "multipart/form-data; boundary=something"; + ApplicationLoadBalancerRequestEvent request = new AwsProxyRequestBuilder(ENCODED_REQUEST_PATH, "GET").header(HttpHeaders.CONTENT_TYPE, contentType).toAlbRequest(); + try { + HttpServletRequest servletRequest = reader.readRequest(request, null, null, ContainerConfig.defaultConfig()); + assertNotNull(servletRequest); + assertNotNull(servletRequest.getHeader(HttpHeaders.CONTENT_TYPE)); + String contentAndCharset = contentType + "; charset=" + LambdaContainerHandler.getContainerConfig().getDefaultContentCharset(); + assertEquals(contentAndCharset, servletRequest.getHeader(HttpHeaders.CONTENT_TYPE)); + assertEquals(LambdaContainerHandler.getContainerConfig().getDefaultContentCharset(), servletRequest.getCharacterEncoding()); + } catch (InvalidRequestEventException e) { + e.printStackTrace(); + fail("Could not read request"); + } + } + + @Test + void readRequest_validEventEmptyPath_expectException() { + try { + ApplicationLoadBalancerRequestEvent req = new AwsProxyRequestBuilder(null, "GET").toAlbRequest(); + HttpServletRequest servletReq = reader.readRequest(req, null, null, ContainerConfig.defaultConfig()); + assertNotNull(servletReq); + } catch (InvalidRequestEventException e) { + e.printStackTrace(); + fail("Could not read a request with a null path"); + } + } + + @Test + void readRequest_invalidEventEmptyMethod_expectException() { + try { + ApplicationLoadBalancerRequestEvent req = new AwsProxyRequestBuilder("/path", null).toAlbRequest(); + reader.readRequest(req, null, null, ContainerConfig.defaultConfig()); + fail("Expected InvalidRequestEventException"); + } catch (InvalidRequestEventException e) { + assertEquals(AwsAlbHttpServletRequestReader.INVALID_REQUEST_ERROR, e.getMessage()); + } + } + + @Test + void readRequest_invalidEventEmptyContext_expectException() { + try { + ApplicationLoadBalancerRequestEvent req = new AwsProxyRequestBuilder("/path", "GET").toAlbRequest(); + req.setRequestContext(null); + reader.readRequest(req, null, null, ContainerConfig.defaultConfig()); + fail("Expected InvalidRequestEventException"); + } catch (InvalidRequestEventException e) { + assertEquals(AwsAlbHttpServletRequestReader.INVALID_REQUEST_ERROR, e.getMessage()); + } + } + + @Test + void readRequest_nullHeaders_expectSuccess() { + ApplicationLoadBalancerRequestEvent req = new AwsProxyRequestBuilder("/path", "GET").toAlbRequest(); + req.setMultiValueHeaders(null); + try { + HttpServletRequest servletReq = reader.readRequest(req, null, null, ContainerConfig.defaultConfig()); + String headerValue = servletReq.getHeader(HttpHeaders.CONTENT_TYPE); + assertNull(headerValue); + } catch (InvalidRequestEventException e) { + e.printStackTrace(); + fail("Failed to read request with null headers"); + } + } + + @Test + void readRequest_emptyHeaders_expectSuccess() { + ApplicationLoadBalancerRequestEvent req = new AwsProxyRequestBuilder("/path", "GET").toAlbRequest(); + try { + HttpServletRequest servletReq = reader.readRequest(req, null, null, ContainerConfig.defaultConfig()); + String headerValue = servletReq.getHeader(HttpHeaders.CONTENT_TYPE); + assertNull(headerValue); + } catch (InvalidRequestEventException e) { + e.printStackTrace(); + fail("Failed to read request with null headers"); + } + } +} \ No newline at end of file diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletResponseWriterTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletResponseWriterTest.java new file mode 100644 index 000000000..d5d9ff91c --- /dev/null +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletResponseWriterTest.java @@ -0,0 +1,44 @@ +package com.amazonaws.serverless.proxy.internal.servlet; + +import com.amazonaws.serverless.exceptions.InvalidRequestEventException; +import com.amazonaws.serverless.exceptions.InvalidResponseObjectException; +import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; +import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext; +import com.amazonaws.serverless.proxy.model.AwsProxyResponse; +import com.amazonaws.serverless.proxy.model.ContainerConfig; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; +import jakarta.servlet.http.HttpServletRequest; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.CountDownLatch; + +import static org.junit.jupiter.api.Assertions.*; + +public class AwsAlbHttpServletResponseWriterTest { + private AwsAlbHttpServletRequestReader reader = new AwsAlbHttpServletRequestReader(); + static AwsAlbHttpServletRequestReader requestReader = new AwsAlbHttpServletRequestReader(); + + @Test + void writeResponse_returnsValidResponse() throws InvalidRequestEventException, InvalidResponseObjectException { + AwsAlbHttpServletResponseWriter responseWriter = new AwsAlbHttpServletResponseWriter(true); + ApplicationLoadBalancerRequestEvent albRequest = new AwsProxyRequestBuilder("/hello", "GET").toAlbRequest(); + HttpServletRequest servletRequest = requestReader.readRequest(albRequest, null, new MockLambdaContext(), ContainerConfig.defaultConfig()); + AwsHttpServletResponse response = new AwsHttpServletResponse(servletRequest, new CountDownLatch(1)); + response.setAwsResponseBodyString("Random string"); + + AwsProxyResponse res3 = responseWriter.writeResponse(response, new MockLambdaContext()); + assertTrue(res3.getHeaders().isEmpty()); + + response.setContentType("application/octet-stream"); + AwsProxyResponse res2 = responseWriter.writeResponse(response, new MockLambdaContext()); + assertTrue(res2.isBase64Encoded()); + + response.setHeader("Connection", "Keep-Alive"); + response.setContentType("application/octet-stream;"); + response.setStatus(200); + AwsProxyResponse res1 = responseWriter.writeResponse(response, new MockLambdaContext()); + assertNotNull(res1); + assertEquals(200, res1.getStatusCode()); + } + +} diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbSecurityContextTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbSecurityContextTest.java new file mode 100644 index 000000000..d4f2ebf6a --- /dev/null +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbSecurityContextTest.java @@ -0,0 +1,65 @@ +package com.amazonaws.serverless.proxy.internal.servlet; + +import static com.amazonaws.serverless.proxy.internal.jaxrs.AwsProxySecurityContext.*; +import static org.junit.jupiter.api.Assertions.*; + +import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; +import org.junit.jupiter.api.Test; + + +public class AwsAlbSecurityContextTest { + private static final ApplicationLoadBalancerRequestEvent ALB_REQUEST_NO_AUTH = new AwsProxyRequestBuilder("/hello", "GET").toAlbRequest(); + private static final String COGNITO_IDENTITY_ID = "us-east-2:123123123123"; + private static final ApplicationLoadBalancerRequestEvent ALB_REQUEST_COGNITO_USER_POOL = new AwsProxyRequestBuilder("/hello", "GET") + .header(ALB_ACESS_TOKEN_HEADER, "xxxxx") + .header(ALB_IDENTITY_HEADER, COGNITO_IDENTITY_ID) + .toAlbRequest(); + + private static final ApplicationLoadBalancerRequestEvent ALB_REQUEST_COGNITO_USER_POOL_NO_IDENTITY_HEADER = new AwsProxyRequestBuilder("/hello", "GET") + .header(ALB_ACESS_TOKEN_HEADER, "xxxxx") + .toAlbRequest(); + + + @Test + void localVars_constructor_nullValues() { + AwsAlbSecurityContext context = new AwsAlbSecurityContext(null, null); + assertNull(context.getEvent()); + assertNull(context.getLambdaContext()); + } + @Test + void alb_noAuth_expectEmptyScheme() { + AwsAlbSecurityContext context = new AwsAlbSecurityContext(null, ALB_REQUEST_NO_AUTH); + assertEquals(ALB_REQUEST_NO_AUTH, context.getEvent()); + assertNull(context.getLambdaContext()); + assertFalse(context.isSecure()); + assertNull(context.getAuthenticationScheme()); + } + + @Test + void alb_cognitoAuth_expectCustomSchemeAndCorrectPrincipal() { + AwsAlbSecurityContext context = new AwsAlbSecurityContext(null, ALB_REQUEST_COGNITO_USER_POOL); + assertTrue(context.isSecure()); + assertEquals(AUTH_SCHEME_CUSTOM, context.getAuthenticationScheme()); + assertEquals(COGNITO_IDENTITY_ID, context.getUserPrincipal().getName()); + } + + @Test + void getUserPrincipal_nullScheme_returnsNull() { + AwsAlbSecurityContext context = new AwsAlbSecurityContext(null, ALB_REQUEST_NO_AUTH); + assertNull(context.getUserPrincipal().getName()); + } + + @Test + void cognitoAuth_noIdentityHeader_returnsNull() { + AwsAlbSecurityContext context = new AwsAlbSecurityContext(null, ALB_REQUEST_COGNITO_USER_POOL_NO_IDENTITY_HEADER); + assertNull(context.getUserPrincipal().getName()); + } + + @Test + void alb_expectNoUserInRole() { + AwsAlbSecurityContext context = new AwsAlbSecurityContext(null, ALB_REQUEST_NO_AUTH); + assertFalse(context.isUserInRole("Role")); + } + +} diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbSecurityContextWriterTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbSecurityContextWriterTest.java new file mode 100644 index 000000000..96a997d5f --- /dev/null +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbSecurityContextWriterTest.java @@ -0,0 +1,39 @@ +package com.amazonaws.serverless.proxy.internal.servlet; + +import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; +import jakarta.ws.rs.core.SecurityContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertFalse; + +public class AwsAlbSecurityContextWriterTest { + private AwsAlbSecurityContextWriter writer; + + @BeforeEach + public void setUp() { + writer = new AwsAlbSecurityContextWriter(); + } + + @Test + void write_returnClass_securityContext() + throws NoSuchMethodException { + Method writeMethod = writer.getClass().getMethod("writeSecurityContext", ApplicationLoadBalancerRequestEvent.class, Context.class); + assertEquals(SecurityContext.class, writeMethod.getReturnType()); + } + + @Test + void write_noAuth_emptySecurityContext() { + ApplicationLoadBalancerRequestEvent request = new AwsProxyRequestBuilder("/test").toAlbRequest(); + SecurityContext context = writer.writeSecurityContext(request, null); + + assertNotNull(context); + assertNull(context.getAuthenticationScheme()); + assertFalse(context.isSecure()); + } +} diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAsyncContextTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAsyncContextTest.java index 2177fa8bb..4c969875f 100644 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAsyncContextTest.java +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAsyncContextTest.java @@ -7,9 +7,9 @@ import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext; -import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import org.junit.jupiter.api.Test; import jakarta.servlet.AsyncContext; @@ -86,13 +86,13 @@ private AwsServletContext getCtx() { return ctx; } - public static class MockContainerHandler extends AwsLambdaServletContainerHandler { + public static class MockContainerHandler extends AwsLambdaServletContainerHandler { private int desiredStatus; private HttpServletResponse response; private Servlet selectedServlet; public MockContainerHandler() { - super(AwsProxyRequest.class, AwsProxyResponse.class, new AwsProxyHttpServletRequestReader(), new AwsProxyHttpServletResponseWriter(), new AwsProxySecurityContextWriter(), new AwsProxyExceptionHandler()); + super(APIGatewayProxyRequestEvent.class, AwsProxyResponse.class, new AwsProxyHttpServletRequestReader(), new AwsProxyHttpServletResponseWriter(), new AwsProxySecurityContextWriter(), new AwsProxyExceptionHandler()); desiredStatus = 200; } diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletRequestTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletRequestTest.java index 2bc433041..20ab0eaa7 100644 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletRequestTest.java +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletRequestTest.java @@ -1,47 +1,44 @@ package com.amazonaws.serverless.proxy.internal.servlet; -import com.amazonaws.serverless.proxy.model.AwsProxyRequest; -import com.amazonaws.serverless.proxy.model.MultiValuedTreeMap; import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext; import com.amazonaws.serverless.proxy.model.ContainerConfig; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import org.junit.jupiter.api.Test; import jakarta.servlet.ServletException; import jakarta.servlet.http.Cookie; import jakarta.ws.rs.core.HttpHeaders; +import static com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequest.findKey; import static org.junit.jupiter.api.Assertions.*; -import java.util.Base64; -import java.util.List; -import java.util.Map; -import java.util.Arrays; +import java.util.*; public class AwsHttpServletRequestTest { - private static final AwsProxyRequest contentTypeRequest = new AwsProxyRequestBuilder("/test", "GET") + private static final APIGatewayProxyRequestEvent contentTypeRequest = new AwsProxyRequestBuilder("/test", "GET") .header(HttpHeaders.CONTENT_TYPE, "application/xml; charset=utf-8").build(); - private static final AwsProxyRequest validCookieRequest = new AwsProxyRequestBuilder("/cookie", "GET") + private static final APIGatewayProxyRequestEvent validCookieRequest = new AwsProxyRequestBuilder("/cookie", "GET") .header(HttpHeaders.COOKIE, "yummy_cookie=choco; tasty_cookie=strawberry").build(); - private static final AwsProxyRequest complexAcceptHeader = new AwsProxyRequestBuilder("/accept", "GET") + private static final APIGatewayProxyRequestEvent complexAcceptHeader = new AwsProxyRequestBuilder("/accept", "GET") .header(HttpHeaders.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8").build(); - private static final AwsProxyRequest queryString = new AwsProxyRequestBuilder("/test", "GET") + private static final APIGatewayProxyRequestEvent queryString = new AwsProxyRequestBuilder("/test", "GET") .queryString("one", "two").queryString("three", "four").build(); - private static final AwsProxyRequest queryStringNullValue = new AwsProxyRequestBuilder("/test", "GET") + private static final APIGatewayProxyRequestEvent queryStringNullValue = new AwsProxyRequestBuilder("/test", "GET") .queryString("one", "two").queryString("three", null).build(); - private static final AwsProxyRequest encodedQueryString = new AwsProxyRequestBuilder("/test", "GET") + private static final APIGatewayProxyRequestEvent encodedQueryString = new AwsProxyRequestBuilder("/test", "GET") .queryString("one", "two").queryString("json", "{\"name\":\"faisal\"}").build(); - private static final AwsProxyRequest multipleParams = new AwsProxyRequestBuilder("/test", "GET") + private static final APIGatewayProxyRequestEvent multipleParams = new AwsProxyRequestBuilder("/test", "GET") .queryString("one", "two").queryString("one", "three").queryString("json", "{\"name\":\"faisal\"}").build(); - private static final AwsProxyRequest formEncodedAndQueryString = new AwsProxyRequestBuilder("/test", "POST") + private static final APIGatewayProxyRequestEvent formEncodedAndQueryString = new AwsProxyRequestBuilder("/test", "POST") .queryString("one", "two").queryString("one", "three") .queryString("five", "six") .form("one", "four") .form("seven", "eight").build(); - private static final AwsProxyRequest differentCasing = new AwsProxyRequestBuilder("/test", "POST") + private static final APIGatewayProxyRequestEvent differentCasing = new AwsProxyRequestBuilder("/test", "POST") .queryString("one", "two").queryString("one", "three") .queryString("ONE", "four").build(); @@ -98,7 +95,7 @@ void headers_parseHeaderValue_encodedContentWithEquals() { void headers_parseHeaderValue_base64EncodedCookieValue() { String value = Base64.getUrlEncoder().encodeToString("a".getBytes()); String cookieValue = "jwt=" + value + "; secondValue=second"; - AwsProxyRequest req = new AwsProxyRequestBuilder("/test", "GET").header(HttpHeaders.COOKIE, cookieValue).build(); + APIGatewayProxyRequestEvent req = new AwsProxyRequestBuilder("/test", "GET").header(HttpHeaders.COOKIE, cookieValue).build(); AwsHttpServletRequest context = new AwsProxyHttpServletRequest(req, null, null); Cookie[] cookies = context.getCookies(); @@ -111,7 +108,7 @@ void headers_parseHeaderValue_base64EncodedCookieValue() { @Test void headers_parseHeaderValue_cookieWithSeparatorInValue() { String cookieValue = "jwt==test; secondValue=second"; - AwsProxyRequest req = new AwsProxyRequestBuilder("/test", "GET").header(HttpHeaders.COOKIE, cookieValue).build(); + APIGatewayProxyRequestEvent req = new AwsProxyRequestBuilder("/test", "GET").header(HttpHeaders.COOKIE, cookieValue).build(); AwsHttpServletRequest context = new AwsProxyHttpServletRequest(req, null, null); Cookie[] cookies = context.getCookies(); @@ -297,10 +294,11 @@ void parameterMap_generateParameterMap_differentCasing() { @Test void queryParamValues_getQueryParamValues() { - AwsProxyHttpServletRequest request = new AwsProxyHttpServletRequest(new AwsProxyRequest(), mockContext, null); - MultiValuedTreeMap map = new MultiValuedTreeMap<>(); - map.add("test", "test"); - map.add("test", "test2"); + AwsProxyHttpServletRequest request = new AwsProxyHttpServletRequest(new APIGatewayProxyRequestEvent(), mockContext, null); + Map> map = new HashMap<>(); + List values = findKey(map, "test"); + values.add("test"); + values.add("test2"); String[] result1 = request.getQueryParamValues(map, "test", true); assertArrayEquals(new String[]{"test", "test2"}, result1); String[] result2 = request.getQueryParamValues(map, "TEST", true); @@ -309,10 +307,11 @@ void queryParamValues_getQueryParamValues() { @Test void queryParamValues_getQueryParamValues_caseInsensitive() { - AwsProxyHttpServletRequest request = new AwsProxyHttpServletRequest(new AwsProxyRequest(), mockContext, null); - MultiValuedTreeMap map = new MultiValuedTreeMap<>(); - map.add("test", "test"); - map.add("test", "test2"); + AwsProxyHttpServletRequest request = new AwsProxyHttpServletRequest(new APIGatewayProxyRequestEvent(), mockContext, null); + Map> map = new HashMap<>(); + List values = findKey(map, "test"); + values.add("test"); + values.add("test2"); String[] result1 = request.getQueryParamValues(map, "test", false); assertArrayEquals(new String[]{"test", "test2"}, result1); String[] result2 = request.getQueryParamValues(map, "TEST", false); diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestFormTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestFormTest.java index 989066328..33a3856da 100644 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestFormTest.java +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestFormTest.java @@ -1,9 +1,9 @@ package com.amazonaws.serverless.proxy.internal.servlet; -import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import org.apache.commons.io.IOUtils; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; @@ -54,7 +54,7 @@ public class AwsProxyHttpServletRequestFormTest { @Test void postForm_getParam_getEncodedFullValue() { try { - AwsProxyRequest proxyRequest = new AwsProxyRequestBuilder("/form", "POST") + APIGatewayProxyRequestEvent proxyRequest = new AwsProxyRequestBuilder("/form", "POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED) .body(ENCODED_FORM_ENTITY) .build(); @@ -70,7 +70,7 @@ void postForm_getParam_getEncodedFullValue() { @Test void postForm_getParts_parsing() { try { - AwsProxyRequest proxyRequest = new AwsProxyRequestBuilder("/form", "POST") + APIGatewayProxyRequestEvent proxyRequest = new AwsProxyRequestBuilder("/form", "POST") .header(HttpHeaders.CONTENT_TYPE, MULTIPART_FORM_DATA.getContentType()) //.header(formData.getContentEncoding().getName(), formData.getContentEncoding().getValue()) .body(IOUtils.toString(MULTIPART_FORM_DATA.getContent(), Charset.defaultCharset())) @@ -89,7 +89,7 @@ void postForm_getParts_parsing() { @Test void multipart_getParts_binary() { try { - AwsProxyRequest proxyRequest = new AwsProxyRequestBuilder("/form", "POST") + APIGatewayProxyRequestEvent proxyRequest = new AwsProxyRequestBuilder("/form", "POST") .header(HttpHeaders.CONTENT_TYPE, MULTIPART_BINARY_DATA.getContentType()) .header(HttpHeaders.CONTENT_LENGTH, MULTIPART_BINARY_DATA.getContentLength() + "") .binaryBody(MULTIPART_BINARY_DATA.getContent()) @@ -111,7 +111,7 @@ void multipart_getParts_binary() { @Test void postForm_getParamsBase64Encoded_expectAllParams() { - AwsProxyRequest proxyRequest = new AwsProxyRequestBuilder("/form", "POST") + APIGatewayProxyRequestEvent proxyRequest = new AwsProxyRequestBuilder("/form", "POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED).build(); proxyRequest.setBody(Base64.getEncoder().encodeToString(ENCODED_FORM_ENTITY.getBytes(Charset.defaultCharset()))); proxyRequest.setIsBase64Encoded(true); @@ -129,7 +129,7 @@ void postForm_getParamsBase64Encoded_expectAllParams() { */ @Test void postForm_emptyParamPresent() { - AwsProxyRequest proxyRequest = new AwsProxyRequestBuilder("/form", "POST") + APIGatewayProxyRequestEvent proxyRequest = new AwsProxyRequestBuilder("/form", "POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED).build(); String body = PART_KEY_1 + "=" + "&" + PART_KEY_2 + "=" + PART_VALUE_2; proxyRequest.setBody(body); diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestReaderTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestReaderTest.java index f92c37168..a0e01e348 100644 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestReaderTest.java +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestReaderTest.java @@ -3,14 +3,13 @@ import com.amazonaws.serverless.exceptions.InvalidRequestEventException; import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; -import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.ContainerConfig; import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import org.junit.jupiter.api.Test; import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.core.HttpHeaders; -import jakarta.ws.rs.core.SecurityContext; import static org.junit.jupiter.api.Assertions.*; @@ -25,7 +24,7 @@ public class AwsProxyHttpServletRequestReaderTest { @Test void readRequest_validAwsProxy_populatedRequest() { - AwsProxyRequest request = new AwsProxyRequestBuilder("/path", "GET").header(TEST_HEADER_KEY, TEST_HEADER_VALUE).build(); + APIGatewayProxyRequestEvent request = new AwsProxyRequestBuilder("/path", "GET").header(TEST_HEADER_KEY, TEST_HEADER_VALUE).build(); try { HttpServletRequest servletRequest = reader.readRequest(request, null, null, ContainerConfig.defaultConfig()); assertNotNull(servletRequest.getHeader(TEST_HEADER_KEY)); @@ -38,7 +37,7 @@ void readRequest_validAwsProxy_populatedRequest() { @Test void readRequest_urlDecode_expectDecodedPath() { - AwsProxyRequest request = new AwsProxyRequestBuilder(ENCODED_REQUEST_PATH, "GET").build(); + APIGatewayProxyRequestEvent request = new AwsProxyRequestBuilder(ENCODED_REQUEST_PATH, "GET").build(); try { HttpServletRequest servletRequest = reader.readRequest(request, null, null, ContainerConfig.defaultConfig()); assertNotNull(servletRequest); @@ -54,7 +53,7 @@ void readRequest_urlDecode_expectDecodedPath() { @Test void readRequest_contentCharset_doesNotOverrideRequestCharset() { String requestCharset = "application/json; charset=UTF-8"; - AwsProxyRequest request = new AwsProxyRequestBuilder(ENCODED_REQUEST_PATH, "GET").header(HttpHeaders.CONTENT_TYPE, requestCharset).build(); + APIGatewayProxyRequestEvent request = new AwsProxyRequestBuilder(ENCODED_REQUEST_PATH, "GET").header(HttpHeaders.CONTENT_TYPE, requestCharset).build(); try { HttpServletRequest servletRequest = reader.readRequest(request, null, null, ContainerConfig.defaultConfig()); assertNotNull(servletRequest); @@ -70,7 +69,7 @@ void readRequest_contentCharset_doesNotOverrideRequestCharset() { @Test void readRequest_contentCharset_setsDefaultCharsetWhenNotSpecified() { String requestCharset = "application/json"; - AwsProxyRequest request = new AwsProxyRequestBuilder(ENCODED_REQUEST_PATH, "GET").header(HttpHeaders.CONTENT_TYPE, requestCharset).build(); + APIGatewayProxyRequestEvent request = new AwsProxyRequestBuilder(ENCODED_REQUEST_PATH, "GET").header(HttpHeaders.CONTENT_TYPE, requestCharset).build(); try { HttpServletRequest servletRequest = reader.readRequest(request, null, null, ContainerConfig.defaultConfig()); assertNotNull(servletRequest); @@ -87,7 +86,7 @@ void readRequest_contentCharset_setsDefaultCharsetWhenNotSpecified() { @Test void readRequest_contentCharset_appendsCharsetToComplextContentType() { String contentType = "multipart/form-data; boundary=something"; - AwsProxyRequest request = new AwsProxyRequestBuilder(ENCODED_REQUEST_PATH, "GET").header(HttpHeaders.CONTENT_TYPE, contentType).build(); + APIGatewayProxyRequestEvent request = new AwsProxyRequestBuilder(ENCODED_REQUEST_PATH, "GET").header(HttpHeaders.CONTENT_TYPE, contentType).build(); try { HttpServletRequest servletRequest = reader.readRequest(request, null, null, ContainerConfig.defaultConfig()); assertNotNull(servletRequest); @@ -104,7 +103,7 @@ void readRequest_contentCharset_appendsCharsetToComplextContentType() { @Test void readRequest_validEventEmptyPath_expectException() { try { - AwsProxyRequest req = new AwsProxyRequestBuilder(null, "GET").build(); + APIGatewayProxyRequestEvent req = new AwsProxyRequestBuilder(null, "GET").build(); HttpServletRequest servletReq = reader.readRequest(req, null, null, ContainerConfig.defaultConfig()); assertNotNull(servletReq); } catch (InvalidRequestEventException e) { @@ -116,7 +115,7 @@ void readRequest_validEventEmptyPath_expectException() { @Test void readRequest_invalidEventEmptyMethod_expectException() { try { - AwsProxyRequest req = new AwsProxyRequestBuilder("/path", null).build(); + APIGatewayProxyRequestEvent req = new AwsProxyRequestBuilder("/path", null).build(); reader.readRequest(req, null, null, ContainerConfig.defaultConfig()); fail("Expected InvalidRequestEventException"); } catch (InvalidRequestEventException e) { @@ -127,7 +126,7 @@ void readRequest_invalidEventEmptyMethod_expectException() { @Test void readRequest_invalidEventEmptyContext_expectException() { try { - AwsProxyRequest req = new AwsProxyRequestBuilder("/path", "GET").build(); + APIGatewayProxyRequestEvent req = new AwsProxyRequestBuilder("/path", "GET").build(); req.setRequestContext(null); reader.readRequest(req, null, null, ContainerConfig.defaultConfig()); fail("Expected InvalidRequestEventException"); @@ -138,7 +137,7 @@ void readRequest_invalidEventEmptyContext_expectException() { @Test void readRequest_nullHeaders_expectSuccess() { - AwsProxyRequest req = new AwsProxyRequestBuilder("/path", "GET").build(); + APIGatewayProxyRequestEvent req = new AwsProxyRequestBuilder("/path", "GET").build(); req.setMultiValueHeaders(null); try { HttpServletRequest servletReq = reader.readRequest(req, null, null, ContainerConfig.defaultConfig()); @@ -152,7 +151,7 @@ void readRequest_nullHeaders_expectSuccess() { @Test void readRequest_emptyHeaders_expectSuccess() { - AwsProxyRequest req = new AwsProxyRequestBuilder("/path", "GET").build(); + APIGatewayProxyRequestEvent req = new AwsProxyRequestBuilder("/path", "GET").build(); try { HttpServletRequest servletReq = reader.readRequest(req, null, null, ContainerConfig.defaultConfig()); String headerValue = servletReq.getHeader(HttpHeaders.CONTENT_TYPE); diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestTest.java index 855fae403..9ad6fb437 100644 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestTest.java +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestTest.java @@ -1,10 +1,10 @@ package com.amazonaws.serverless.proxy.internal.servlet; import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; -import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -66,7 +66,7 @@ public class AwsProxyHttpServletRequestTest { private static final AwsProxyRequestBuilder REQUEST_NULL_QUERY_STRING; static { - AwsProxyRequest awsProxyRequest = new AwsProxyRequestBuilder("/hello", "GET").build(); + APIGatewayProxyRequestEvent awsProxyRequest = new AwsProxyRequestBuilder("/hello", "GET").build(); awsProxyRequest.setMultiValueQueryStringParameters(null); REQUEST_NULL_QUERY_STRING = new AwsProxyRequestBuilder(awsProxyRequest); } @@ -90,7 +90,7 @@ private HttpServletRequest getRequest(AwsProxyRequestBuilder req, Context lambda case "API_GW": return new AwsProxyHttpServletRequest(req.build(), lambdaCtx, securityCtx); case "ALB": - return new AwsProxyHttpServletRequest(req.alb().build(), lambdaCtx, securityCtx); + return new AwsAlbHttpServletRequest(req.toAlbRequest(), lambdaCtx, securityCtx); case "HTTP_API": return new AwsHttpApiV2ProxyHttpServletRequest(req.toHttpApiV2Request(), lambdaCtx, securityCtx, LambdaContainerHandler.getContainerConfig()); case "WRAP": diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyRequestDispatcherTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyRequestDispatcherTest.java index 254ce04a0..dd83099b1 100644 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyRequestDispatcherTest.java +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyRequestDispatcherTest.java @@ -6,10 +6,10 @@ import com.amazonaws.serverless.proxy.AwsProxySecurityContextWriter; import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext; -import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; import com.amazonaws.serverless.proxy.model.ContainerConfig; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import org.junit.jupiter.api.Test; import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper; @@ -30,7 +30,7 @@ public class AwsProxyRequestDispatcherTest { @Test void setPath_forwardByPath_proxyRequestObjectInPropertyReferencesSameProxyRequest() throws InvalidRequestEventException { - AwsProxyRequest proxyRequest = new AwsProxyRequestBuilder("/hello", "GET").build(); + APIGatewayProxyRequestEvent proxyRequest = new AwsProxyRequestBuilder("/hello", "GET").build(); HttpServletRequest servletRequest = requestReader.readRequest(proxyRequest, null, new MockLambdaContext(), ContainerConfig.defaultConfig()); AwsProxyRequestDispatcher dispatcher = new AwsProxyRequestDispatcher(FORWARD_PATH, false, null); @@ -40,7 +40,7 @@ void setPath_forwardByPath_proxyRequestObjectInPropertyReferencesSameProxyReques @Test void setPathForWrappedRequest_forwardByPath_proxyRequestObjectInPropertyReferencesSameProxyRequest() throws InvalidRequestEventException { - AwsProxyRequest proxyRequest = new AwsProxyRequestBuilder("/hello", "GET").build(); + APIGatewayProxyRequestEvent proxyRequest = new AwsProxyRequestBuilder("/hello", "GET").build(); HttpServletRequest servletRequest = requestReader.readRequest(proxyRequest, null, new MockLambdaContext(), ContainerConfig.defaultConfig()); SecurityContextHolderAwareRequestWrapper springSecurityRequest = new SecurityContextHolderAwareRequestWrapper(servletRequest, "ADMIN"); @@ -51,7 +51,7 @@ void setPathForWrappedRequest_forwardByPath_proxyRequestObjectInPropertyReferenc @Test void setPathForWrappedRequestWithoutGatewayEvent_forwardByPath_throwsException() { - AwsProxyRequest proxyRequest = new AwsProxyRequestBuilder("/hello", "GET").build(); + APIGatewayProxyRequestEvent proxyRequest = new AwsProxyRequestBuilder("/hello", "GET").build(); AwsProxyHttpServletRequest servletRequest = new AwsProxyHttpServletRequest(proxyRequest, new MockLambdaContext(), null); SecurityContextHolderAwareRequestWrapper springSecurityRequest = new SecurityContextHolderAwareRequestWrapper(servletRequest, "ADMIN"); @@ -67,7 +67,7 @@ void setPathForWrappedRequestWithoutGatewayEvent_forwardByPath_throwsException() @Test void forwardRequest_nullHandler_throwsIllegalStateException() throws InvalidRequestEventException { - AwsProxyRequest proxyRequest = new AwsProxyRequestBuilder("/hello", "GET").build(); + APIGatewayProxyRequestEvent proxyRequest = new AwsProxyRequestBuilder("/hello", "GET").build(); HttpServletRequest servletRequest = requestReader.readRequest(proxyRequest, null, new MockLambdaContext(), ContainerConfig.defaultConfig()); AwsProxyRequestDispatcher dispatcher = new AwsProxyRequestDispatcher(FORWARD_PATH, false, null); try { @@ -85,7 +85,7 @@ void forwardRequest_nullHandler_throwsIllegalStateException() throws InvalidRequ @Test void forwardRequest_committedResponse_throwsIllegalStateException() throws InvalidRequestEventException { - AwsProxyRequest proxyRequest = new AwsProxyRequestBuilder("/hello", "GET").build(); + APIGatewayProxyRequestEvent proxyRequest = new AwsProxyRequestBuilder("/hello", "GET").build(); HttpServletRequest servletRequest = requestReader.readRequest(proxyRequest, null, new MockLambdaContext(), ContainerConfig.defaultConfig()); AwsProxyRequestDispatcher dispatcher = new AwsProxyRequestDispatcher(FORWARD_PATH, false, mockLambdaHandler(null)); AwsHttpServletResponse resp = new AwsHttpServletResponse(servletRequest, new CountDownLatch(1)); @@ -106,7 +106,7 @@ void forwardRequest_committedResponse_throwsIllegalStateException() throws Inval @Test void forwardRequest_partiallyWrittenResponse_resetsBuffer() throws InvalidRequestEventException { - AwsProxyRequest proxyRequest = new AwsProxyRequestBuilder("/hello", "GET").build(); + APIGatewayProxyRequestEvent proxyRequest = new AwsProxyRequestBuilder("/hello", "GET").build(); HttpServletRequest servletRequest = requestReader.readRequest(proxyRequest, null, new MockLambdaContext(), ContainerConfig.defaultConfig()); AwsProxyRequestDispatcher dispatcher = new AwsProxyRequestDispatcher(FORWARD_PATH, false, mockLambdaHandler(null)); AwsHttpServletResponse resp = new AwsHttpServletResponse(servletRequest, new CountDownLatch(1)); @@ -128,7 +128,7 @@ void forwardRequest_partiallyWrittenResponse_resetsBuffer() throws InvalidReques void include_addsToResponse_appendsCorrectly() throws InvalidRequestEventException, IOException { final String firstPart = "first"; final String secondPart = "second"; - AwsProxyRequest proxyRequest = new AwsProxyRequestBuilder("/hello", "GET").build(); + APIGatewayProxyRequestEvent proxyRequest = new AwsProxyRequestBuilder("/hello", "GET").build(); AwsProxyResponse resp = mockLambdaHandler((AwsProxyHttpServletRequest req, AwsHttpServletResponse res) -> { if (req.getAttribute("cnt") == null) { @@ -149,7 +149,7 @@ void include_appendsNewHeader_cannotAppendNewHeaders() throws InvalidRequestEven final String firstPart = "first"; final String secondPart = "second"; final String headerKey = "X-Custom-Header"; - AwsProxyRequest proxyRequest = new AwsProxyRequestBuilder("/hello", "GET").build(); + APIGatewayProxyRequestEvent proxyRequest = new AwsProxyRequestBuilder("/hello", "GET").build(); AwsProxyResponse resp = mockLambdaHandler((AwsProxyHttpServletRequest req, AwsHttpServletResponse res) -> { if (req.getAttribute("cnt") == null) { @@ -172,9 +172,9 @@ private interface RequestHandler { } - private AwsLambdaServletContainerHandler mockLambdaHandler(RequestHandler h) { - return new AwsLambdaServletContainerHandler( - AwsProxyRequest.class, + private AwsLambdaServletContainerHandler mockLambdaHandler(RequestHandler h) { + return new AwsLambdaServletContainerHandler( + APIGatewayProxyRequestEvent.class, AwsProxyResponse.class, new AwsProxyHttpServletRequestReader(), new AwsProxyHttpServletResponseWriter(), diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/ServletLambdaContainerHandlerBuilderTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/ServletLambdaContainerHandlerBuilderTest.java index 73f8d7908..e6ad77bc0 100644 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/ServletLambdaContainerHandlerBuilderTest.java +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/ServletLambdaContainerHandlerBuilderTest.java @@ -3,9 +3,9 @@ import com.amazonaws.serverless.exceptions.ContainerInitializationException; import com.amazonaws.serverless.proxy.AwsProxyExceptionHandler; import com.amazonaws.serverless.proxy.AwsProxySecurityContextWriter; -import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import org.junit.jupiter.api.Test; import jakarta.servlet.http.HttpServletRequest; @@ -40,15 +40,15 @@ void defaultProxy_setsValuesCorrectly() { assertTrue(test.requestReader instanceof AwsProxyHttpServletRequestReader); assertTrue(test.responseWriter instanceof AwsProxyHttpServletResponseWriter); assertTrue(test.securityContextWriter instanceof AwsProxySecurityContextWriter); - assertSame(AwsProxyRequest.class, test.requestTypeClass); + assertSame(APIGatewayProxyRequestEvent.class, test.requestTypeClass); assertSame(AwsProxyResponse.class, test.responseTypeClass); assertEquals("test", test.name); } - public static final class TestHandler extends AwsLambdaServletContainerHandler { + public static final class TestHandler extends AwsLambdaServletContainerHandler { public TestHandler() { - super(AwsProxyRequest.class, AwsProxyResponse.class, new AwsProxyHttpServletRequestReader(), new AwsProxyHttpServletResponseWriter(), new AwsProxySecurityContextWriter(), new AwsProxyExceptionHandler()); + super(APIGatewayProxyRequestEvent.class, AwsProxyResponse.class, new AwsProxyHttpServletRequestReader(), new AwsProxyHttpServletResponseWriter(), new AwsProxySecurityContextWriter(), new AwsProxyExceptionHandler()); } @Override protected AwsHttpServletResponse getContainerResponse(HttpServletRequest request, CountDownLatch latch) { @@ -68,7 +68,7 @@ public void initialize() throws ContainerInitializationException { public static final class TestBuilder extends ServletLambdaContainerHandlerBuilder< - AwsProxyRequest, + APIGatewayProxyRequestEvent, AwsProxyResponse, HttpServletRequest, TestHandler, diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java index ee48d6d1d..13d178ee1 100644 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java @@ -15,6 +15,8 @@ import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; import com.amazonaws.serverless.proxy.model.*; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import com.fasterxml.jackson.core.JsonProcessingException; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -38,6 +40,8 @@ import java.nio.charset.StandardCharsets; import java.util.*; +import static com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper.*; + /** * Request builder object. This is used by unit proxy to quickly create an AWS_PROXY request object @@ -48,7 +52,7 @@ public class AwsProxyRequestBuilder { // Variables - Private //------------------------------------------------------------- - private AwsProxyRequest request; + private APIGatewayProxyRequestEvent request; private MultipartEntityBuilder multipartBuilder; //------------------------------------------------------------- @@ -64,26 +68,27 @@ public AwsProxyRequestBuilder(String path) { this(path, null); } - public AwsProxyRequestBuilder(AwsProxyRequest req) { + public AwsProxyRequestBuilder(APIGatewayProxyRequestEvent req) { request = req; } public AwsProxyRequestBuilder(String path, String httpMethod) { - this.request = new AwsProxyRequest(); + this.request = new APIGatewayProxyRequestEvent(); this.request.setMultiValueHeaders(new Headers()); // avoid NPE this.request.setHttpMethod(httpMethod); this.request.setPath(path); this.request.setMultiValueQueryStringParameters(new MultiValuedTreeMap<>()); - this.request.setRequestContext(new AwsProxyRequestContext()); + this.request.setRequestContext(new APIGatewayProxyRequestEvent.ProxyRequestContext()); this.request.getRequestContext().setRequestId(UUID.randomUUID().toString()); this.request.getRequestContext().setExtendedRequestId(UUID.randomUUID().toString()); this.request.getRequestContext().setStage("test"); this.request.getRequestContext().setProtocol("HTTP/1.1"); this.request.getRequestContext().setRequestTimeEpoch(System.currentTimeMillis()); - ApiGatewayRequestIdentity identity = new ApiGatewayRequestIdentity(); + APIGatewayProxyRequestEvent.RequestIdentity identity = new APIGatewayProxyRequestEvent.RequestIdentity(); identity.setSourceIp("127.0.0.1"); this.request.getRequestContext().setIdentity(identity); + this.request.setIsBase64Encoded(false); } @@ -91,32 +96,26 @@ public AwsProxyRequestBuilder(String path, String httpMethod) { // Methods - Public //------------------------------------------------------------- - public AwsProxyRequestBuilder alb() { - this.request.setRequestContext(new AwsProxyRequestContext()); - this.request.getRequestContext().setElb(new AlbContext()); - this.request.getRequestContext().getElb().setTargetGroupArn( - "arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/lambda-target/d6190d154bc908a5" - ); + public ApplicationLoadBalancerRequestEvent toAlbRequest() { + ApplicationLoadBalancerRequestEvent req = new ApplicationLoadBalancerRequestEvent(); - // ALB does not decode query string parameters so we re-encode them all - if (request.getMultiValueQueryStringParameters() != null) { - MultiValuedTreeMap newQs = new MultiValuedTreeMap<>(); - for (Map.Entry> e : request.getMultiValueQueryStringParameters().entrySet()) { - for (String v : e.getValue()) { - try { - // this is a terrible hack. In our Spring tests we use the comma as a control character for lists - // this is allowed by the HTTP specs although not recommended. - String key = URLEncoder.encode(e.getKey(), "UTF-8").replaceAll("%2C", ","); - String value = URLEncoder.encode(v, "UTF-8").replaceAll("%2C", ","); - newQs.add(key, value); - } catch (UnsupportedEncodingException ex) { - throw new RuntimeException("Could not encode query string parameters: " + e.getKey() + "=" + v, ex); - } - } - } - request.setMultiValueQueryStringParameters(newQs); - } - return this; + ApplicationLoadBalancerRequestEvent.Elb elb = new ApplicationLoadBalancerRequestEvent.Elb(); + elb.setTargetGroupArn("arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/lambda-target/d6190d154bc908a5"); + + ApplicationLoadBalancerRequestEvent.RequestContext requestContext = new ApplicationLoadBalancerRequestEvent.RequestContext(); + requestContext.setElb(elb); + + req.setRequestContext(requestContext); + req.setHttpMethod(request.getHttpMethod()); + req.setPath(request.getPath()); + req.setQueryStringParameters(request.getQueryStringParameters()); + req.setMultiValueQueryStringParameters(request.getMultiValueQueryStringParameters()); + req.setHeaders(request.getHeaders()); + req.setMultiValueHeaders(request.getMultiValueHeaders()); + req.setBody(request.getBody()); + req.setIsBase64Encoded(request.getIsBase64Encoded()); + + return req; } public AwsProxyRequestBuilder stage(String stageName) { @@ -145,7 +144,10 @@ public AwsProxyRequestBuilder form(String key, String value) { if (request.getMultiValueHeaders() == null) { request.setMultiValueHeaders(new Headers()); } - request.getMultiValueHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED); + if (request.getMultiValueHeaders().get(HttpHeaders.CONTENT_TYPE) == null) { + request.getMultiValueHeaders().put(HttpHeaders.CONTENT_TYPE, new ArrayList<>()); + } + request.getMultiValueHeaders().get(HttpHeaders.CONTENT_TYPE).add(MediaType.APPLICATION_FORM_URLENCODED); String body = request.getBody(); if (body == null) { body = ""; @@ -200,7 +202,13 @@ public AwsProxyRequestBuilder header(String key, String value) { this.request.setMultiValueHeaders(new Headers()); } - this.request.getMultiValueHeaders().add(key, value); + if (this.request.getMultiValueHeaders().get(key) != null) { + this.request.getMultiValueHeaders().get(key).add(value); + } else { + List values = new ArrayList<>(); + values.add(value); + this.request.getMultiValueHeaders().put(key, values); + } return this; } @@ -217,28 +225,15 @@ public AwsProxyRequestBuilder multiValueQueryString(MultiValuedTreeMap()); + this.request.setMultiValueQueryStringParameters(new HashMap>()); } - if (request.getRequestSource() == RequestSource.API_GATEWAY) { - this.request.getMultiValueQueryStringParameters().add(key, value); - } - // ALB does not decode parameters automatically like API Gateway. - if (request.getRequestSource() == RequestSource.ALB) { - try { - //if (URLDecoder.decode(value, ContainerConfig.DEFAULT_CONTENT_CHARSET).equals(value)) { - // TODO: Assume we are always given an unencoded value, smarter check here to encode - // only if necessary - this.request.getMultiValueQueryStringParameters().add( - key, - URLEncoder.encode(value, ContainerConfig.DEFAULT_CONTENT_CHARSET) - ); - //} - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - + List values = this.request.getMultiValueQueryStringParameters().get(key); + if (values == null) { + values = new ArrayList<>(); } + values.add(value); + this.request.getMultiValueQueryStringParameters().put(key, values); return this; } @@ -254,7 +249,7 @@ public AwsProxyRequestBuilder nullBody() { } public AwsProxyRequestBuilder body(Object body) { - if (request.getMultiValueHeaders() != null && request.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE).startsWith(MediaType.APPLICATION_JSON)) { + if (request.getMultiValueHeaders() != null && request.getMultiValueHeaders().get(HttpHeaders.CONTENT_TYPE).get(0).startsWith(MediaType.APPLICATION_JSON)) { try { return body(LambdaContainerHandler.getObjectMapper().writeValueAsString(body)); } catch (JsonProcessingException e) { @@ -267,7 +262,7 @@ public AwsProxyRequestBuilder body(Object body) { public AwsProxyRequestBuilder apiId(String id) { if (request.getRequestContext() == null) { - request.setRequestContext(new AwsProxyRequestContext()); + request.setRequestContext(new APIGatewayProxyRequestEvent.ProxyRequestContext()); } request.getRequestContext().setApiId(id); return this; @@ -281,37 +276,20 @@ public AwsProxyRequestBuilder binaryBody(InputStream is) public AwsProxyRequestBuilder authorizerPrincipal(String principal) { - if (this.request.getRequestSource() == RequestSource.API_GATEWAY) { - if (this.request.getRequestContext().getAuthorizer() == null) { - this.request.getRequestContext().setAuthorizer(new ApiGatewayAuthorizerContext()); - } - this.request.getRequestContext().getAuthorizer().setPrincipalId(principal); - if (this.request.getRequestContext().getAuthorizer().getClaims() == null) { - this.request.getRequestContext().getAuthorizer().setClaims(new CognitoAuthorizerClaims()); - } - this.request.getRequestContext().getAuthorizer().getClaims().setSubject(principal); - } - if (this.request.getRequestSource() == RequestSource.ALB) { - header("x-amzn-oidc-identity", principal); - try { - header( - "x-amzn-oidc-accesstoken", - Base64.getMimeEncoder().encodeToString( - "test-token".getBytes(ContainerConfig.DEFAULT_CONTENT_CHARSET) - ) - ); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } + if (this.request.getRequestContext().getAuthorizer() == null) { + this.request.getRequestContext().setAuthorizer(new HashMap()); } + this.request.getRequestContext().getAuthorizer().put("principalId", principal); + this.request.getRequestContext().getAuthorizer().computeIfAbsent("claims", k -> new HashMap()); + ((Map) this.request.getRequestContext().getAuthorizer().get("claims")).put("sub", principal); return this; } public AwsProxyRequestBuilder authorizerContextValue(String key, String value) { if (this.request.getRequestContext().getAuthorizer() == null) { - this.request.getRequestContext().setAuthorizer(new ApiGatewayAuthorizerContext()); + this.request.getRequestContext().setAuthorizer(new HashMap<>()); } - this.request.getRequestContext().getAuthorizer().setContextValue(key, value); + this.request.getRequestContext().getAuthorizer().put(key, value); return this; } @@ -320,17 +298,16 @@ public AwsProxyRequestBuilder cognitoUserPool(String identityId) { this.request.getRequestContext().getIdentity().setCognitoAuthenticationType("POOL"); this.request.getRequestContext().getIdentity().setCognitoIdentityId(identityId); if (this.request.getRequestContext().getAuthorizer() == null) { - this.request.getRequestContext().setAuthorizer(new ApiGatewayAuthorizerContext()); + this.request.getRequestContext().setAuthorizer(new HashMap<>()); } - this.request.getRequestContext().getAuthorizer().setClaims(new CognitoAuthorizerClaims()); - this.request.getRequestContext().getAuthorizer().getClaims().setSubject(identityId); + this.request.getRequestContext().getAuthorizer().put("claims", new HashMap<>()); + ((Map) this.request.getRequestContext().getAuthorizer().get("claims")).put("sub", identityId); return this; } public AwsProxyRequestBuilder claim(String claim, String value) { - this.request.getRequestContext().getAuthorizer().getClaims().setClaim(claim, value); - + ((Map) this.request.getRequestContext().getAuthorizer().get("claims")).put(claim, value); return this; } @@ -347,14 +324,13 @@ public AwsProxyRequestBuilder cookie(String name, String value) { if (request.getMultiValueHeaders() == null) { request.setMultiValueHeaders(new Headers()); } - - String cookies = request.getMultiValueHeaders().getFirst(HttpHeaders.COOKIE); + String cookies = getFirst(request.getMultiValueHeaders(), HttpHeaders.COOKIE); if (cookies == null) { cookies = ""; } cookies += (cookies.equals("")?"":"; ") + name + "=" + value; - request.getMultiValueHeaders().putSingle(HttpHeaders.COOKIE, cookies); + putSingle(request.getMultiValueHeaders(), HttpHeaders.COOKIE, cookies); return this; } @@ -362,8 +338,7 @@ public AwsProxyRequestBuilder scheme(String scheme) { if (request.getMultiValueHeaders() == null) { request.setMultiValueHeaders(new Headers()); } - - request.getMultiValueHeaders().putSingle("CloudFront-Forwarded-Proto", scheme); + putSingle(request.getMultiValueHeaders(),"CloudFront-Forwarded-Proto", scheme); return this; } @@ -371,17 +346,16 @@ public AwsProxyRequestBuilder serverName(String serverName) { if (request.getMultiValueHeaders() == null) { request.setMultiValueHeaders(new Headers()); } - - request.getMultiValueHeaders().putSingle("Host", serverName); + putSingle(request.getMultiValueHeaders(), "Host", serverName); return this; } public AwsProxyRequestBuilder userAgent(String agent) { if (request.getRequestContext() == null) { - request.setRequestContext(new AwsProxyRequestContext()); + request.setRequestContext(new APIGatewayProxyRequestEvent.ProxyRequestContext()); } if (request.getRequestContext().getIdentity() == null) { - request.getRequestContext().setIdentity(new ApiGatewayRequestIdentity()); + request.getRequestContext().setIdentity(new APIGatewayProxyRequestEvent.RequestIdentity()); } request.getRequestContext().getIdentity().setUserAgent(agent); @@ -390,10 +364,10 @@ public AwsProxyRequestBuilder userAgent(String agent) { public AwsProxyRequestBuilder referer(String referer) { if (request.getRequestContext() == null) { - request.setRequestContext(new AwsProxyRequestContext()); + request.setRequestContext(new APIGatewayProxyRequestEvent.ProxyRequestContext()); } if (request.getRequestContext().getIdentity() == null) { - request.getRequestContext().setIdentity(new ApiGatewayRequestIdentity()); + request.getRequestContext().setIdentity(new APIGatewayProxyRequestEvent.RequestIdentity()); } request.getRequestContext().getIdentity().setCaller(referer); @@ -405,24 +379,34 @@ public AwsProxyRequestBuilder basicAuth(String username, String password) { // we remove the existing authorization strategy request.getMultiValueHeaders().remove(HttpHeaders.AUTHORIZATION); String authHeader = "Basic " + Base64.getMimeEncoder().encodeToString((username + ":" + password).getBytes(Charset.defaultCharset())); - request.getMultiValueHeaders().add(HttpHeaders.AUTHORIZATION, authHeader); + List values = findKey(request.getMultiValueHeaders(), HttpHeaders.AUTHORIZATION); + values.add(authHeader); return this; } public AwsProxyRequestBuilder fromJsonString(String jsonContent) throws IOException { - request = LambdaContainerHandler.getObjectMapper().readValue(jsonContent, AwsProxyRequest.class); + request = LambdaContainerHandler.getObjectMapper().readValue(jsonContent, APIGatewayProxyRequestEvent.class); + makeHeadersCaseInsensitive(request); return this; } + private void makeHeadersCaseInsensitive(APIGatewayProxyRequestEvent request) { + Headers newHeaders = new Headers(); + if (Objects.nonNull(request.getMultiValueHeaders())) { + newHeaders.putAll(request.getMultiValueHeaders()); + request.setMultiValueHeaders(newHeaders); + } + } + @SuppressFBWarnings("PATH_TRAVERSAL_IN") public AwsProxyRequestBuilder fromJsonPath(String filePath) throws IOException { - request = LambdaContainerHandler.getObjectMapper().readValue(new File(filePath), AwsProxyRequest.class); + request = LambdaContainerHandler.getObjectMapper().readValue(new File(filePath), APIGatewayProxyRequestEvent.class); return this; } - public AwsProxyRequest build() { + public APIGatewayProxyRequestEvent build() { return this.request; } @@ -435,6 +419,16 @@ public InputStream buildStream() { } } + public InputStream toAlbRequestStream() { + ApplicationLoadBalancerRequestEvent req = toAlbRequest(); + try { + String requestJson = LambdaContainerHandler.getObjectMapper().writeValueAsString(req); + return new ByteArrayInputStream(requestJson.getBytes(StandardCharsets.UTF_8)); + } catch (JsonProcessingException e) { + return null; + } + } + public InputStream toHttpApiV2RequestStream() { APIGatewayV2HTTPEvent req = toHttpApiV2Request(); try { @@ -448,7 +442,7 @@ public InputStream toHttpApiV2RequestStream() { public APIGatewayV2HTTPEvent toHttpApiV2Request() { APIGatewayV2HTTPEvent req = new APIGatewayV2HTTPEvent(); req.setRawPath(request.getPath()); - req.setIsBase64Encoded(request.isBase64Encoded()); + req.setIsBase64Encoded(request.getIsBase64Encoded()); req.setBody(request.getBody()); if (request.getMultiValueHeaders() != null && request.getMultiValueHeaders().containsKey(HttpHeaders.COOKIE)) { req.setCookies(Arrays.asList(request.getMultiValueHeaders().get(HttpHeaders.COOKIE).get(0).split(";"))); diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/ApiGatewayAuthorizerContextTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/ApiGatewayAuthorizerContextTest.java index 196552c1d..4e9700aa8 100644 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/ApiGatewayAuthorizerContextTest.java +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/ApiGatewayAuthorizerContextTest.java @@ -2,6 +2,7 @@ import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -62,14 +63,12 @@ public class ApiGatewayAuthorizerContextTest { @Test void authorizerContext_serialize_customValues() { try { - AwsProxyRequest req = new AwsProxyRequestBuilder().fromJsonString(AUTHORIZER_REQUEST).build(); - - assertNotNull(req.getRequestContext().getAuthorizer().getContextValue(FIELD_NAME_1)); - assertNotNull(req.getRequestContext().getAuthorizer().getContextValue(FIELD_NAME_2)); - assertEquals(FIELD_VALUE_1, req.getRequestContext().getAuthorizer().getContextValue(FIELD_NAME_1)); - assertEquals(FIELD_VALUE_2, req.getRequestContext().getAuthorizer().getContextValue(FIELD_NAME_2)); - assertEquals(PRINCIPAL, req.getRequestContext().getAuthorizer().getPrincipalId()); - assertNull(req.getRequestContext().getAuthorizer().getContextValue("principalId")); + APIGatewayProxyRequestEvent req = new AwsProxyRequestBuilder().fromJsonString(AUTHORIZER_REQUEST).build(); + assertNotNull(req.getRequestContext().getAuthorizer().get(FIELD_NAME_1)); + assertNotNull(req.getRequestContext().getAuthorizer().get(FIELD_NAME_2)); + assertEquals(FIELD_VALUE_1, req.getRequestContext().getAuthorizer().get(FIELD_NAME_1)); + assertEquals(FIELD_VALUE_2, req.getRequestContext().getAuthorizer().get(FIELD_NAME_2)); + assertEquals(PRINCIPAL, req.getRequestContext().getAuthorizer().get("principalId")); } catch (IOException e) { e.printStackTrace(); fail(); diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/AwsProxyRequestTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/AwsProxyRequestTest.java index 6d0aa9eeb..8ebaa7156 100644 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/AwsProxyRequestTest.java +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/AwsProxyRequestTest.java @@ -5,6 +5,10 @@ import static org.junit.jupiter.api.Assertions.*; import java.io.IOException; + +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; import com.fasterxml.jackson.databind.ObjectMapper; @@ -15,26 +19,26 @@ public class AwsProxyRequestTest { @Test void deserialize_multiValuedHeaders_caseInsensitive() throws IOException { - AwsProxyRequest req = new AwsProxyRequestBuilder() + APIGatewayProxyRequestEvent req = new AwsProxyRequestBuilder() .fromJsonString(getRequestJson(true, CUSTOM_HEADER_KEY_LOWER_CASE, CUSTOM_HEADER_VALUE)).build(); assertNotNull(req.getMultiValueHeaders().get(CUSTOM_HEADER_KEY_LOWER_CASE.toUpperCase())); assertEquals(CUSTOM_HEADER_VALUE, req.getMultiValueHeaders().get(CUSTOM_HEADER_KEY_LOWER_CASE.toUpperCase()).get(0)); - assertTrue(req.isBase64Encoded()); + assertTrue(req.getIsBase64Encoded()); } @Test void deserialize_base64Encoded_readsBoolCorrectly() throws IOException { - AwsProxyRequest req = new AwsProxyRequestBuilder() + APIGatewayProxyRequestEvent req = new AwsProxyRequestBuilder() .fromJsonString(getRequestJson(true, CUSTOM_HEADER_KEY_LOWER_CASE, CUSTOM_HEADER_VALUE)).build(); - assertTrue(req.isBase64Encoded()); + assertTrue(req.getIsBase64Encoded()); req = new AwsProxyRequestBuilder() .fromJsonString(getRequestJson(false, CUSTOM_HEADER_KEY_LOWER_CASE, CUSTOM_HEADER_VALUE)).build(); - assertFalse(req.isBase64Encoded()); + assertFalse(req.getIsBase64Encoded()); } @Test void serialize_base64Encoded_fieldContainsIsPrefix() throws IOException { - AwsProxyRequest req = new AwsProxyRequestBuilder() + APIGatewayProxyRequestEvent req = new AwsProxyRequestBuilder() .fromJsonString(getRequestJson(true, CUSTOM_HEADER_KEY_LOWER_CASE, CUSTOM_HEADER_VALUE)).build(); ObjectMapper mapper = new ObjectMapper(); String serializedRequest = mapper.writeValueAsString(req); @@ -102,35 +106,4 @@ private String getRequestJson(boolean base64Encoded, String headerKey, String he " \"isBase64Encoded\": " + (base64Encoded?"true":"false") + "\n" + "}"; } - - @Test - void deserialize_singleValuedHeaders() throws IOException { - AwsProxyRequest req = - new AwsProxyRequestBuilder().fromJsonString(getSingleValueRequestJson()).build(); - - assertThat(req.getHeaders().get("accept"), is("*")); - } - - /** - * Captured from a live request to an ALB with a Lambda integration with - * lambda.multi_value_headers.enabled=false. - */ - private String getSingleValueRequestJson() { - return "{\n" + " \"requestContext\": {\n" + " \"elb\": {\n" - + " \"targetGroupArn\": \"arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/prod-example-function/e77803ebb6d2c24\"\n" - + " }\n" + " },\n" + " \"httpMethod\": \"PUT\",\n" - + " \"path\": \"/path/to/resource\",\n" + " \"queryStringParameters\": {},\n" - + " \"headers\": {\n" + " \"accept\": \"*\",\n" - + " \"content-length\": \"17\",\n" - + " \"content-type\": \"application/json\",\n" - + " \"host\": \"stackoverflow.name\",\n" - + " \"user-agent\": \"curl/7.77.0\",\n" - + " \"x-amzn-trace-id\": \"Root=1-62e22402-3a5f246225e45edd7735c182\",\n" - + " \"x-forwarded-for\": \"24.14.13.186\",\n" - + " \"x-forwarded-port\": \"443\",\n" - + " \"x-forwarded-proto\": \"https\",\n" - + " \"x-jersey-tracing-accept\": \"true\"\n" + " },\n" - + " \"body\": \"{\\\"alpha\\\":\\\"bravo\\\"}\",\n" - + " \"isBase64Encoded\": false\n" + "} \n"; - } } diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/CognitoAuthorizerClaimsTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/CognitoAuthorizerClaimsTest.java deleted file mode 100644 index 1ad2314b6..000000000 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/CognitoAuthorizerClaimsTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.amazonaws.serverless.proxy.model; - -import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; - -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Locale; - -import static org.junit.jupiter.api.Assertions.*; - -public class CognitoAuthorizerClaimsTest { - - private static final String USERNAME = "test_username"; - private static final String SUB = "42df3b02-29f1-4779-a3e5-eff92ff280b2"; - private static final String AUD = "2k3no2j1rjjbqaskc4bk0ub29b"; - private static final String EMAIL = "testemail@test.com"; - - private static final String EXP_TIME = "Mon Apr 17 23:12:49 UTC 2017"; - private static final String ISSUE_TIME = "Mon Apr 17 22:12:49 UTC 2017"; - static final DateTimeFormatter TOKEN_DATE_FORMATTER = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy").withLocale(Locale.ENGLISH); - - private static final String USER_POOLS_REQUEST = "{\n" - + " \"resource\": \"/restaurants\",\n" - + " \"path\": \"/restaurants\",\n" - + " \"httpMethod\": \"GET\",\n" - + " \"headers\": {\n" - + " \"Accept\": \"*/*\",\n" - + " \"Authorization\": \"eyJraWQiOiJKSm9VQUtrRThcL3NTU3Rwa3dPZTFWN2dvak1xS0k1NU8zTzB4WVgwMGNRdz0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiI0MmRmM2IwMi0yOWYxLTQ3NzktYTNlNS1lZmY5MmZmMjgwYjIiLCJhdWQiOiIyazNubzJqMXJqamJxYXNrYzRiazB1YjI5YiIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTQ5MjQ2NzE2OSwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLnVzLWVhc3QtMi5hbWF6b25hd3MuY29tXC91cy1lYXN0LTJfQWR4NVpIZVBnIiwiY29nbml0bzp1c2VybmFtZSI6InNhcGVzc2kiLCJleHAiOjE0OTI0NzA3NjksImlhdCI6MTQ5MjQ2NzE2OSwiZW1haWwiOiJidWxpYW5pc0BhbWF6b24uY29tIn0.aTODUMNib_pQhad1aWTHrlz7kwA5QkcvZptcbLFY5BuNqpr9zsK14EhHRvmvflK4MMQaxCE5Cxa9joR9g-HCmmF1usZhXO4Q2iyEWcBk0whjn3CnC55k6yEuMv6y9krts0YHSamsRkhW7wnCpuLmk2KgzHTfyt6oQ1qbg9QE8l9LRhjCHLnujlLIQaG9p9UfJVf-uGSg1k_bCyzl48lqkc7LDwqDZCHXGf1RYRQLg5jphXF_tjByDk_0t9Ah7pX2nFwl0SUz74enG8emq58g4pemeVekb9Mw0wyD-B5TWeGVs_nvmC3q4jgxMyJy3Xq4Ggd9qSgIN_Khdg3Q26F2bA\",\n" - + " \"CloudFront-Forwarded-Proto\": \"https\"\n" - + " },\n" - + " \"queryStringParameters\": null,\n" - + " \"pathParameters\": null,\n" - + " \"stageVariables\": null,\n" - + " \"requestContext\": {\n" - + " \"accountId\": \"XXXXXXXXXXXXXX\",\n" - + " \"resourceId\": \"xxxxx\",\n" - + " \"stage\": \"dev\",\n" - + " \"authorizer\": {\n" - + " \"claims\": {\n" - + " \"sub\": \"" + SUB + "\",\n" - + " \"aud\": \"" + AUD + "\",\n" - + " \"email_verified\": \"true\",\n" - + " \"token_use\": \"id\",\n" - + " \"auth_time\": \"1492467169\",\n" - + " \"iss\": \"https://cognito-idp.us-east-2.amazonaws.com/us-east-2_xxXXxxXX\",\n" - + " \"cognito:username\": \"" + USERNAME + "\",\n" - + " \"exp\": \"" + EXP_TIME + "\",\n" - + " \"iat\": \"" + ISSUE_TIME + "\",\n" - + " \"email\": \"" + EMAIL + "\"\n" - + " }\n" - + " },\n" - + " \"requestId\": \"ad0a33ba-23bc-11e7-9b7d-235a67eb05bd\",\n" - + " \"identity\": {\n" - + " \"cognitoIdentityPoolId\": null,\n" - + " \"accountId\": null,\n" - + " \"cognitoIdentityId\": null,\n" - + " \"caller\": null,\n" - + " \"apiKey\": null,\n" - + " \"sourceIp\": \"54.240.196.171\",\n" - + " \"accessKey\": null,\n" - + " \"cognitoAuthenticationType\": null,\n" - + " \"cognitoAuthenticationProvider\": null,\n" - + " \"userArn\": null,\n" - + " \"userAgent\": \"PostmanRuntime/3.0.1\",\n" - + " \"user\": null\n" - + " },\n" - + " \"resourcePath\": \"/restaurants\",\n" - + " \"httpMethod\": \"GET\",\n" - + " \"apiId\": \"xxxxxxxx\"\n" - + " },\n" - + " \"body\": null,\n" - + " \"isBase64Encoded\": false\n" - + "}"; - - - @Test - void claims_serialize_validJsonString() { - try { - AwsProxyRequest req = new AwsProxyRequestBuilder().fromJsonString(USER_POOLS_REQUEST).build(); - - assertEquals(USERNAME, req.getRequestContext().getAuthorizer().getClaims().getUsername()); - assertEquals(EMAIL, req.getRequestContext().getAuthorizer().getClaims().getEmail()); - assertTrue(req.getRequestContext().getAuthorizer().getClaims().isEmailVerified()); - } catch (IOException e) { - fail(); - } - } - - @Test - void claims_dateParse_issueTime() { - try { - AwsProxyRequest req = new AwsProxyRequestBuilder().fromJsonString(USER_POOLS_REQUEST).build(); - - assertEquals(EXP_TIME, req.getRequestContext().getAuthorizer().getClaims().getExpiration()); - assertNotNull(req.getRequestContext().getAuthorizer().getClaims().getExpiration()); - - ZonedDateTime expTime = ZonedDateTime.from(TOKEN_DATE_FORMATTER.parse(EXP_TIME)); - ZonedDateTime issueTime = ZonedDateTime.from(TOKEN_DATE_FORMATTER.parse(ISSUE_TIME)); - assertEquals(expTime, ZonedDateTime.from(TOKEN_DATE_FORMATTER.parse(req.getRequestContext().getAuthorizer().getClaims().getExpiration()))); - - assertEquals(expTime, issueTime.plusHours(1)); - } catch (IOException e) { - e.printStackTrace(); - fail(); - } - } -} \ No newline at end of file diff --git a/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyLambdaContainerHandler.java b/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyLambdaContainerHandler.java index 8b38886e8..8334a6203 100644 --- a/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyLambdaContainerHandler.java +++ b/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyLambdaContainerHandler.java @@ -19,11 +19,12 @@ import com.amazonaws.serverless.proxy.jersey.suppliers.AwsProxyServletContextSupplier; import com.amazonaws.serverless.proxy.jersey.suppliers.AwsProxyServletRequestSupplier; import com.amazonaws.serverless.proxy.jersey.suppliers.AwsProxyServletResponseSupplier; -import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; +import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent; import org.glassfish.jersey.internal.inject.AbstractBinder; import org.glassfish.jersey.internal.inject.InjectionManager; @@ -50,11 +51,11 @@ * *
  * {@code
- *   public class LambdaHandler implements RequestHandler {
+ *   public class LambdaHandler implements RequestHandler {
  *     private ResourceConfig jerseyApplication = new ResourceConfig().packages("your.app.package");
  *     private JerseyLambdaContainerHandler container = JerseyLambdaContainerHandler.getAwsProxyHandler(jerseyApplication);
  *
- *     public AwsProxyResponse handleRequest(AwsProxyRequest awsProxyRequest, Context context) {
+ *     public AwsProxyResponse handleRequest(APIGatewayProxyRequestEvent awsProxyRequest, Context context) {
  *       return container.proxy(awsProxyRequest, context);
  *     }
  *   }
@@ -81,16 +82,16 @@ public class JerseyLambdaContainerHandler extends Aws
 
     /**
      * Returns an initialized JerseyLambdaContainerHandler that includes RequestReader and
-     * ResponseWriter objects for the AwsProxyRequest and AwsProxyResponse
+     * ResponseWriter objects for the APIGatewayProxyRequestEvent and AwsProxyResponse
      * objects.
      *
      * @param jaxRsApplication A configured Jax-Rs application object. For Jersey apps this can be the default
      *                         ResourceConfig object
      * @return A JerseyLambdaContainerHandler object
      */
-    public static JerseyLambdaContainerHandler getAwsProxyHandler(Application jaxRsApplication) {
-        JerseyLambdaContainerHandler newHandler = new JerseyLambdaContainerHandler<>(
-                AwsProxyRequest.class,
+    public static JerseyLambdaContainerHandler getAwsProxyHandler(Application jaxRsApplication) {
+        JerseyLambdaContainerHandler newHandler = new JerseyLambdaContainerHandler<>(
+                APIGatewayProxyRequestEvent.class,
                 AwsProxyResponse.class,
                 new AwsProxyHttpServletRequestReader(),
                 new AwsProxyHttpServletResponseWriter(),
@@ -123,6 +124,19 @@ public static JerseyLambdaContainerHandler getAlbProxyHandler(Application jaxRsApplication) {
+        JerseyLambdaContainerHandler newHandler = new JerseyLambdaContainerHandler<>(
+                ApplicationLoadBalancerRequestEvent.class,
+                AwsProxyResponse.class,
+                new AwsAlbHttpServletRequestReader(),
+                new AwsProxyHttpServletResponseWriter(true),
+                new AwsAlbSecurityContextWriter(),
+                new AwsAlbExceptionHandler(),
+                jaxRsApplication);
+        newHandler.initialize();
+        return newHandler;
+    }
+
 
     //-------------------------------------------------------------
     // Constructors
diff --git a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/EchoJerseyResource.java b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/EchoJerseyResource.java
index d05c6814f..c3ab2dac8 100644
--- a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/EchoJerseyResource.java
+++ b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/EchoJerseyResource.java
@@ -14,10 +14,10 @@
 
 import com.amazonaws.serverless.proxy.RequestReader;
 import com.amazonaws.serverless.proxy.jersey.providers.ServletRequestFilter;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequestContext;
 import com.amazonaws.serverless.proxy.jersey.model.MapResponseModel;
 import com.amazonaws.serverless.proxy.jersey.model.SingleValueModel;
 
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
 import org.glassfish.jersey.media.multipart.FormDataParam;
 
@@ -157,9 +157,9 @@ public SingleValueModel echoRequestScheme(@Context UriInfo context) {
     @Produces(MediaType.APPLICATION_JSON)
     public SingleValueModel echoAuthorizerPrincipal(@Context ContainerRequestContext context) {
         SingleValueModel valueModel = new SingleValueModel();
-        AwsProxyRequestContext awsProxyRequestContext =
-                (AwsProxyRequestContext) context.getProperty(RequestReader.API_GATEWAY_CONTEXT_PROPERTY);
-        valueModel.setValue(awsProxyRequestContext.getAuthorizer().getPrincipalId());
+        APIGatewayProxyRequestEvent.ProxyRequestContext awsProxyRequestContext =
+                (APIGatewayProxyRequestEvent.ProxyRequestContext) context.getProperty(RequestReader.API_GATEWAY_CONTEXT_PROPERTY);
+        valueModel.setValue(awsProxyRequestContext.getAuthorizer().get("principalId").toString());
 
         return valueModel;
     }
@@ -168,9 +168,9 @@ public SingleValueModel echoAuthorizerPrincipal(@Context ContainerRequestContext
     @Produces(MediaType.APPLICATION_JSON)
     public SingleValueModel echoAuthorizerContext(@Context ContainerRequestContext context, @QueryParam("key") String key) {
         SingleValueModel valueModel = new SingleValueModel();
-        AwsProxyRequestContext awsProxyRequestContext =
-                (AwsProxyRequestContext) context.getProperty(RequestReader.API_GATEWAY_CONTEXT_PROPERTY);
-        valueModel.setValue(awsProxyRequestContext.getAuthorizer().getContextValue(key));
+        APIGatewayProxyRequestEvent.ProxyRequestContext awsProxyRequestContext =
+                (APIGatewayProxyRequestEvent.ProxyRequestContext) context.getProperty(RequestReader.API_GATEWAY_CONTEXT_PROPERTY);
+        valueModel.setValue(awsProxyRequestContext.getAuthorizer().get(key).toString());
 
         return valueModel;
     }
diff --git a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyTest.java b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyTest.java
index 7547467b2..cf21288e2 100644
--- a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyTest.java
+++ b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyTest.java
@@ -14,15 +14,17 @@
 
 
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
+import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsServletContext;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
 import com.amazonaws.serverless.proxy.jersey.model.MapResponseModel;
 import com.amazonaws.serverless.proxy.jersey.model.SingleValueModel;
 import com.amazonaws.serverless.proxy.jersey.providers.ServletRequestFilter;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -80,10 +82,18 @@ public class JerseyAwsProxyTest {
     .register(MultiPartFeature.class)
     .property(LoggingFeature.LOGGING_FEATURE_VERBOSITY_SERVER, LoggingFeature.Verbosity.PAYLOAD_ANY);
 
-    private static JerseyLambdaContainerHandler handler;
+    private static ResourceConfig albApp = new ResourceConfig().packages("com.amazonaws.serverless.proxy.jersey")
+            .register(LoggingFeature.class)
+            .register(ServletRequestFilter.class)
+            .register(MultiPartFeature.class)
+            .register(new ResourceBinder())
+            .property(LoggingFeature.LOGGING_FEATURE_VERBOSITY_SERVER, LoggingFeature.Verbosity.PAYLOAD_ANY);
+
+    private static JerseyLambdaContainerHandler handler;
     private static JerseyLambdaContainerHandler httpApiHandler;
+    private static JerseyLambdaContainerHandler albHandler;
 
-    private static JerseyLambdaContainerHandler handlerWithoutRegisteredDependencies
+    private static JerseyLambdaContainerHandler handlerWithoutRegisteredDependencies
     = JerseyLambdaContainerHandler.getAwsProxyHandler(appWithoutRegisteredDependencies);
 
     private static Context lambdaContext = new MockLambdaContext();
@@ -111,10 +121,10 @@ private AwsProxyResponse executeRequest(AwsProxyRequestBuilder requestBuilder, C
                 }
                 return handler.proxy(requestBuilder.build(), lambdaContext);
             case "ALB":
-                if (handler == null) {
-                    handler = JerseyLambdaContainerHandler.getAwsProxyHandler(app);
+                if (albHandler == null) {
+                    albHandler = JerseyLambdaContainerHandler.getAlbProxyHandler(albApp);
                 }
-                return handler.proxy(requestBuilder.alb().build(), lambdaContext);
+                return albHandler.proxy(requestBuilder.toAlbRequest(), lambdaContext);
             case "HTTP_API":
                 if (httpApiHandler == null) {
                     httpApiHandler = JerseyLambdaContainerHandler.getHttpApiV2ProxyHandler(httpApiApp);
@@ -147,7 +157,7 @@ void headers_getHeaders_echo(String reqType) {
 
         AwsProxyResponse output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", output.getMultiValueHeaders().getFirst("Content-Type"));
+        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
         validateMapResponseModel(output);
     }
@@ -162,7 +172,7 @@ void headers_servletRequest_echo(String reqType) {
 
         AwsProxyResponse output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", output.getMultiValueHeaders().getFirst("Content-Type"));
+        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
         validateMapResponseModel(output);
     }
@@ -172,13 +182,13 @@ void headers_servletRequest_echo(String reqType) {
     void headers_servletRequest_failedDependencyInjection_expectInternalServerError(String reqType) {
         initJerseyAwsProxyTest(reqType);
         assumeTrue("API_GW".equals(type));
-        AwsProxyRequest request = getRequestBuilder("/echo/servlet-headers", "GET")
+        APIGatewayProxyRequestEvent request = getRequestBuilder("/echo/servlet-headers", "GET")
         .json()
         .header(CUSTOM_HEADER_KEY, CUSTOM_HEADER_VALUE)
         .build();
 
         AwsProxyResponse output = handlerWithoutRegisteredDependencies.proxy(request, lambdaContext);
-        assertEquals("application/json", output.getMultiValueHeaders().getFirst("Content-Type"));
+        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
         assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), output.getStatusCode());
     }
 
@@ -201,7 +211,7 @@ void context_serverInfo_correctContext(String reqType) {
         AwsProxyRequestBuilder request = getRequestBuilder("/echo/servlet-context", "GET");
         AwsProxyResponse output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", output.getMultiValueHeaders().getFirst("Content-Type"));
+        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
         validateSingleValueModel(output, AwsServletContext.SERVER_INFO);
     }
@@ -215,7 +225,7 @@ void requestScheme_valid_expectHttps(String reqType) {
 
         AwsProxyResponse output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", output.getMultiValueHeaders().getFirst("Content-Type"));
+        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
         validateSingleValueModel(output, "https");
     }
@@ -229,7 +239,7 @@ void requestFilter_injectsServletRequest_expectCustomAttribute(String reqType) {
 
         AwsProxyResponse output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", output.getMultiValueHeaders().getFirst("Content-Type"));
+        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
         validateSingleValueModel(output, ServletRequestFilter.FILTER_ATTRIBUTE_VALUE);
     }
@@ -245,7 +255,7 @@ void authorizer_securityContext_customPrincipalSuccess(String reqType) {
 
         AwsProxyResponse output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", output.getMultiValueHeaders().getFirst("Content-Type"));
+        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
         validateSingleValueModel(output, AUTHORIZER_PRINCIPAL_ID);
     }
 
@@ -262,7 +272,7 @@ void authorizer_securityContext_customAuthorizerContextSuccess(String reqType) {
 
         AwsProxyResponse output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", output.getMultiValueHeaders().getFirst("Content-Type"));
+        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
         validateSingleValueModel(output, CUSTOM_HEADER_VALUE);
     }
diff --git a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyInjectionTest.java b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyInjectionTest.java
index e253f34a3..8d9802c51 100644
--- a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyInjectionTest.java
+++ b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyInjectionTest.java
@@ -15,13 +15,13 @@
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import jakarta.inject.Singleton;
 
 import org.glassfish.jersey.internal.inject.AbstractBinder;
 import org.glassfish.jersey.media.multipart.MultiPartFeature;
 import org.glassfish.jersey.server.ResourceConfig;
 import org.junit.jupiter.api.Test;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 
 /**
@@ -42,7 +42,7 @@ protected void configure() {
     private static ResourceConfig app = new ResourceConfig().register(MultiPartFeature.class)
                                                             .register(new ResourceBinder());
 
-    private static JerseyLambdaContainerHandler handler = JerseyLambdaContainerHandler.getAwsProxyHandler(
+    private static JerseyLambdaContainerHandler handler = JerseyLambdaContainerHandler.getAwsProxyHandler(
             app);
 
     @Test
diff --git a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyParamEncodingTest.java b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyParamEncodingTest.java
index 5fe808db2..bdf11a4bc 100644
--- a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyParamEncodingTest.java
+++ b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyParamEncodingTest.java
@@ -2,16 +2,20 @@
 
 
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
+import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
 import com.amazonaws.serverless.proxy.jersey.model.MapResponseModel;
 import com.amazonaws.serverless.proxy.jersey.model.SingleValueModel;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
+import com.amazonaws.serverless.proxy.jersey.providers.ServletRequestFilter;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.services.lambda.runtime.Context;
 
+import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import org.glassfish.jersey.logging.LoggingFeature;
 import org.glassfish.jersey.media.multipart.MultiPartFeature;
 import org.glassfish.jersey.server.ResourceConfig;
 import org.junit.jupiter.api.Disabled;
@@ -66,7 +70,7 @@ public class JerseyParamEncodingTest {
     .register(new ResourceBinder())
     .property("jersey.config.server.tracing.type", "ALL")
     .property("jersey.config.server.tracing.threshold", "VERBOSE");
-    private static JerseyLambdaContainerHandler handler;
+    private static JerseyLambdaContainerHandler handler;
 
     private static ResourceConfig httpApiApp = new ResourceConfig().packages("com.amazonaws.serverless.proxy.jersey")
     .register(MultiPartFeature.class)
@@ -75,6 +79,14 @@ public class JerseyParamEncodingTest {
     .property("jersey.config.server.tracing.threshold", "VERBOSE");
     private static JerseyLambdaContainerHandler httpApiHandler;
 
+    private static ResourceConfig albApp = new ResourceConfig().packages("com.amazonaws.serverless.proxy.jersey")
+            .register(LoggingFeature.class)
+            .register(ServletRequestFilter.class)
+            .register(MultiPartFeature.class)
+            .register(new ResourceBinder())
+            .property(LoggingFeature.LOGGING_FEATURE_VERBOSITY_SERVER, LoggingFeature.Verbosity.PAYLOAD_ANY);
+    private static JerseyLambdaContainerHandler albHandler;
+
     private static Context lambdaContext = new MockLambdaContext();
 
     private String type;
@@ -100,10 +112,10 @@ private AwsProxyResponse executeRequest(AwsProxyRequestBuilder requestBuilder, C
                 }
                 return handler.proxy(requestBuilder.build(), lambdaContext);
             case "ALB":
-                if (handler == null) {
-                    handler = JerseyLambdaContainerHandler.getAwsProxyHandler(app);
+                if (albHandler == null) {
+                    albHandler = JerseyLambdaContainerHandler.getAlbProxyHandler(albApp);
                 }
-                return handler.proxy(requestBuilder.alb().build(), lambdaContext);
+                return albHandler.proxy(requestBuilder.toAlbRequest(), lambdaContext);
             case "HTTP_API":
                 if (httpApiHandler == null) {
                     httpApiHandler = JerseyLambdaContainerHandler.getHttpApiV2ProxyHandler(httpApiApp);
@@ -124,7 +136,7 @@ void queryString_uriInfo_echo(String reqType) {
 
         AwsProxyResponse output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", output.getMultiValueHeaders().getFirst("Content-Type"));
+        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
         validateMapResponseModel(output, QUERY_STRING_KEY, QUERY_STRING_NON_ENCODED_VALUE);
     }
@@ -139,7 +151,7 @@ void queryString_notEncoded_echo(String reqType) {
 
         AwsProxyResponse output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", output.getMultiValueHeaders().getFirst("Content-Type"));
+        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
         validateMapResponseModel(output, QUERY_STRING_KEY, QUERY_STRING_NON_ENCODED_VALUE);
     }
@@ -155,7 +167,7 @@ void queryString_encoded_echo(String reqType) {
 
         AwsProxyResponse output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", output.getMultiValueHeaders().getFirst("Content-Type"));
+        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
         validateMapResponseModel(output, QUERY_STRING_KEY, QUERY_STRING_NON_ENCODED_VALUE);
     }
diff --git a/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringLambdaContainerHandler.java b/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringLambdaContainerHandler.java
index f09ffa598..00d9d49fa 100644
--- a/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringLambdaContainerHandler.java
+++ b/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringLambdaContainerHandler.java
@@ -15,10 +15,11 @@
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.*;
 import com.amazonaws.serverless.proxy.internal.testutils.Timer;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.internal.servlet.*;
 import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent;
 import org.springframework.web.context.ConfigurableWebApplicationContext;
 import org.springframework.web.servlet.DispatcherServlet;
@@ -44,13 +45,13 @@ public class SpringLambdaContainerHandler extends Aws
     private boolean refreshContext = false;
 
     /**
-     * Creates a default SpringLambdaContainerHandler initialized with the `AwsProxyRequest` and `AwsProxyResponse` objects
+     * Creates a default SpringLambdaContainerHandler initialized with the `APIGatewayProxyRequestEvent` and `AwsProxyResponse` objects
      * @param config A set of classes annotated with the Spring @Configuration annotation
      * @return An initialized instance of the `SpringLambdaContainerHandler`
      * @throws ContainerInitializationException When the Spring framework fails to start.
      */
-    public static SpringLambdaContainerHandler getAwsProxyHandler(Class... config) throws ContainerInitializationException {
-        return new SpringProxyHandlerBuilder()
+    public static SpringLambdaContainerHandler getAwsProxyHandler(Class... config) throws ContainerInitializationException {
+        return new SpringProxyHandlerBuilder()
                 .defaultProxy()
                 .initializationWrapper(new InitializationWrapper())
                 .configurationClasses(config)
@@ -58,15 +59,15 @@ public static SpringLambdaContainerHandler ge
     }
 
     /**
-     * Creates a default SpringLambdaContainerHandler initialized with the `AwsProxyRequest` and `AwsProxyResponse` objects and sets the given profiles as active
+     * Creates a default SpringLambdaContainerHandler initialized with the `APIGatewayProxyRequestEvent` and `AwsProxyResponse` objects and sets the given profiles as active
      * @param applicationContext A custom ConfigurableWebApplicationContext to be used
      * @param profiles The spring profiles to activate
      * @return An initialized instance of the `SpringLambdaContainerHandler`
      * @throws ContainerInitializationException When the Spring framework fails to start.
      */
-    public static SpringLambdaContainerHandler getAwsProxyHandler(ConfigurableWebApplicationContext applicationContext, String... profiles)
+    public static SpringLambdaContainerHandler getAwsProxyHandler(ConfigurableWebApplicationContext applicationContext, String... profiles)
             throws ContainerInitializationException {
-        return new SpringProxyHandlerBuilder()
+        return new SpringProxyHandlerBuilder()
                 .defaultProxy()
                 .initializationWrapper(new InitializationWrapper())
                 .springApplicationContext(applicationContext)
@@ -88,6 +89,14 @@ public static SpringLambdaContainerHandler getAlbProxyHandler(Class... config) throws ContainerInitializationException {
+        return new SpringProxyHandlerBuilder()
+                .defaultAlbProxy()
+                .initializationWrapper(new InitializationWrapper())
+                .configurationClasses(config)
+                .buildAndInitialize();
+    }
+
     /**
      * Creates a new container handler with the given reader and writer objects
      *
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SlowAppTest.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SlowAppTest.java
index a33d5374f..17cf703cd 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SlowAppTest.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SlowAppTest.java
@@ -3,15 +3,14 @@
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.springslowapp.LambdaHandler;
 import com.amazonaws.serverless.proxy.spring.springslowapp.MessageController;
 import com.amazonaws.serverless.proxy.spring.springslowapp.SlowAppConfig;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import org.junit.jupiter.api.Test;
 
 import java.time.Instant;
-import java.util.Objects;
 
 import static org.junit.jupiter.api.Assertions.*;
 
@@ -28,7 +27,7 @@ void springSlowApp_continuesInBackgroundThread_returnsCorrect() {
         }
         System.out.println("Start time: " + slowApp.getConstructorTime());
         assertTrue(slowApp.getConstructorTime() < 10_000);
-        AwsProxyRequest req = new AwsProxyRequestBuilder("/hello", "GET").build();
+        APIGatewayProxyRequestEvent req = new AwsProxyRequestBuilder("/hello", "GET").build();
         long startRequestTime = Instant.now().toEpochMilli();
         AwsProxyResponse resp = slowApp.handleRequest(req, new MockLambdaContext());
         long endRequestTime = Instant.now().toEpochMilli();
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringAwsProxyTest.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringAwsProxyTest.java
index cbc269aba..0a20ae012 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringAwsProxyTest.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringAwsProxyTest.java
@@ -2,6 +2,7 @@
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
+import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsLambdaServletContainerHandler;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsServletRegistration;
 import com.amazonaws.serverless.proxy.model.*;
@@ -15,6 +16,8 @@
 import com.amazonaws.serverless.proxy.spring.echoapp.model.MapResponseModel;
 import com.amazonaws.serverless.proxy.spring.echoapp.model.SingleValueModel;
 import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -49,8 +52,9 @@ public class SpringAwsProxyTest {
 
     private ObjectMapper objectMapper = new ObjectMapper();
     private MockLambdaContext lambdaContext = new MockLambdaContext();
-    private static SpringLambdaContainerHandler handler;
+    private static SpringLambdaContainerHandler handler;
     private static SpringLambdaContainerHandler httpApiHandler;
+    private static SpringLambdaContainerHandler albHandler;
 
     private AwsLambdaServletContainerHandler.StartupHandler h = (c -> {
         FilterRegistration.Dynamic registration = c.addFilter("UnauthenticatedFilter", UnauthenticatedFilter.class);
@@ -82,11 +86,11 @@ private AwsProxyResponse executeRequest(AwsProxyRequestBuilder requestBuilder, C
                     }
                     return handler.proxy(requestBuilder.build(), lambdaContext);
                 case "ALB":
-                    if (handler == null) {
-                        handler = SpringLambdaContainerHandler.getAwsProxyHandler(EchoSpringAppConfig.class);
-                        handler.onStartup(h);
+                    if (albHandler == null) {
+                        albHandler = SpringLambdaContainerHandler.getAlbProxyHandler(EchoSpringAppConfig.class);
+                        albHandler.onStartup(h);
                     }
-                    return handler.proxy(requestBuilder.alb().build(), lambdaContext);
+                    return albHandler.proxy(requestBuilder.toAlbRequest(), lambdaContext);
                 case "HTTP_API":
                     if (httpApiHandler == null) {
                         httpApiHandler = SpringLambdaContainerHandler.getHttpApiV2ProxyHandler(EchoSpringAppConfig.class);
@@ -103,6 +107,21 @@ private AwsProxyResponse executeRequest(AwsProxyRequestBuilder requestBuilder, C
         }
     }
 
+    private AwsProxyResponse executeV2Request(AwsProxyRequestBuilder requestBuilder, Context lambdaContext) {
+        try {
+            if (httpApiHandler == null) {
+                httpApiHandler = SpringLambdaContainerHandler.getHttpApiV2ProxyHandler(EchoSpringAppConfig.class);
+                httpApiHandler.onStartup(h);
+            }
+            return httpApiHandler.proxy(requestBuilder.toHttpApiV2Request(), lambdaContext);
+        } catch (ContainerInitializationException e) {
+            e.printStackTrace();
+            fail("Could not execute request");
+            throw new RuntimeException(e);
+        }
+
+    }
+
     @BeforeEach
     public void clearServletContextCache() {
         AwsServletContext.clearServletContextCache();
@@ -133,8 +152,7 @@ void headers_getHeaders_echo(String reqType) {
 
         AwsProxyResponse output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", output.getMultiValueHeaders().getFirst("Content-Type").split(";")[0]);
-
+        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
         validateMapResponseModel(output);
     }
 
@@ -148,7 +166,7 @@ void headers_servletRequest_echo(String reqType) {
 
         AwsProxyResponse output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", output.getMultiValueHeaders().getFirst("Content-Type").split(";")[0]);
+        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
 
         validateMapResponseModel(output);
     }
@@ -163,7 +181,7 @@ void queryString_uriInfo_echo(String reqType) {
 
         AwsProxyResponse output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", output.getMultiValueHeaders().getFirst("Content-Type").split(";")[0]);
+        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
 
         validateMapResponseModel(output);
     }
@@ -244,7 +262,7 @@ void authorizer_securityContext_customPrincipalSuccess(String reqType) {
 
         AwsProxyResponse output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", output.getMultiValueHeaders().getFirst("Content-Type").split(";")[0]);
+        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
 
         validateSingleValueModel(output, AUTHORIZER_PRINCIPAL_ID);
     }
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringServletContextTest.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringServletContextTest.java
index 11b6c7c5f..899f37cb5 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringServletContextTest.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringServletContextTest.java
@@ -2,7 +2,7 @@
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
+import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsServletContext;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
@@ -11,6 +11,7 @@
 import com.amazonaws.serverless.proxy.spring.echoapp.CustomHeaderFilter;
 import com.amazonaws.serverless.proxy.spring.echoapp.EchoSpringAppConfig;
 import com.amazonaws.serverless.proxy.spring.echoapp.model.ValidatedUserModel;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 import org.springframework.http.HttpHeaders;
@@ -29,7 +30,7 @@ public class SpringServletContextTest {
     private static final String STAGE = LambdaContainerHandler.SERVER_INFO + "/" + AwsServletContext.SERVLET_API_MAJOR_VERSION + "." + AwsServletContext.SERVLET_API_MINOR_VERSION;
     private MockLambdaContext lambdaContext = new MockLambdaContext();
 
-    private static SpringLambdaContainerHandler handler;
+    private static SpringLambdaContainerHandler handler;
 
     @BeforeAll
     public static void setUp() {
@@ -50,33 +51,33 @@ public static void setUp() {
 
     @Test
     void context_autowireValidContext_echoContext() {
-        AwsProxyRequest request = new AwsProxyRequestBuilder("/echo/servlet-context", "GET")
+        APIGatewayProxyRequestEvent request = new AwsProxyRequestBuilder("/echo/servlet-context", "GET")
                 .json()
                 .stage(STAGE)
                 .build();
 
         AwsProxyResponse output = handler.proxy(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("text/plain", output.getMultiValueHeaders().getFirst("Content-Type").split(";")[0]);
+        assertEquals("text/plain", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
         assertEquals(STAGE, output.getBody());
     }
 
     @Test
     void context_contextAware_contextEcho() {
-        AwsProxyRequest request = new AwsProxyRequestBuilder("/context/echo", "GET")
+        APIGatewayProxyRequestEvent request = new AwsProxyRequestBuilder("/context/echo", "GET")
                 .json()
                 .stage(STAGE)
                 .build();
 
         AwsProxyResponse output = handler.proxy(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("text/plain", output.getMultiValueHeaders().getFirst("Content-Type").split(";")[0]);
+        assertEquals("text/plain", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
         assertEquals(STAGE, output.getBody());
     }
 
     @Test
     void filter_customHeaderFilter_echoHeaders() {
-        AwsProxyRequest request = new AwsProxyRequestBuilder("/echo/headers", "GET")
+        APIGatewayProxyRequestEvent request = new AwsProxyRequestBuilder("/echo/headers", "GET")
                 .json()
                 .stage(STAGE)
                 .build();
@@ -85,14 +86,14 @@ void filter_customHeaderFilter_echoHeaders() {
         assertNotNull(output.getMultiValueHeaders());
         assertTrue(output.getMultiValueHeaders().size() > 0);
         assertNotNull(output.getMultiValueHeaders().get(CustomHeaderFilter.HEADER_NAME));
-        assertEquals(CustomHeaderFilter.HEADER_VALUE, output.getMultiValueHeaders().getFirst(CustomHeaderFilter.HEADER_NAME));
+        assertEquals(CustomHeaderFilter.HEADER_VALUE, AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), CustomHeaderFilter.HEADER_NAME));
     }
 
     @Test
     void filter_validationFilter_emptyName() {
         ValidatedUserModel userModel = new ValidatedUserModel();
         userModel.setFirstName("Test");
-        AwsProxyRequest request = new AwsProxyRequestBuilder("/context/user", "POST")
+        APIGatewayProxyRequestEvent request = new AwsProxyRequestBuilder("/context/user", "POST")
                 .json()
                 .body(userModel)
                 .build();
@@ -103,7 +104,7 @@ void filter_validationFilter_emptyName() {
 
     @Test
     void exception_populatedException_annotationValuesMappedCorrectly() {
-        AwsProxyRequest request = new AwsProxyRequestBuilder("/context/exception", "GET")
+        APIGatewayProxyRequestEvent request = new AwsProxyRequestBuilder("/context/exception", "GET")
                 .stage(STAGE)
                 .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
                 .build();
@@ -116,7 +117,7 @@ void exception_populatedException_annotationValuesMappedCorrectly() {
 
     @Test
     void cookie_injectInResponse_expectCustomSetCookie() {
-        AwsProxyRequest request = new AwsProxyRequestBuilder("/context/cookie", "GET")
+        APIGatewayProxyRequestEvent request = new AwsProxyRequestBuilder("/context/cookie", "GET")
                 .stage(STAGE)
                 .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
                 .build();
@@ -126,8 +127,9 @@ void cookie_injectInResponse_expectCustomSetCookie() {
 
         assertEquals(200, output.getStatusCode());
         assertTrue(output.getMultiValueHeaders().containsKey(HttpHeaders.SET_COOKIE));
-        assertTrue(output.getMultiValueHeaders().getFirst(HttpHeaders.SET_COOKIE).contains(ContextResource.COOKIE_NAME + "=" + ContextResource.COOKIE_VALUE));
-        assertTrue(output.getMultiValueHeaders().getFirst(HttpHeaders.SET_COOKIE).contains(ContextResource.COOKIE_DOMAIN));
+
+        assertTrue(AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), HttpHeaders.SET_COOKIE).contains(ContextResource.COOKIE_NAME + "=" + ContextResource.COOKIE_VALUE));
+        assertTrue(AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), HttpHeaders.SET_COOKIE).contains(ContextResource.COOKIE_DOMAIN));
     }
 }
 
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/StaticAppProxyTest.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/StaticAppProxyTest.java
index 9e828119f..62e1fc3f5 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/StaticAppProxyTest.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/StaticAppProxyTest.java
@@ -2,12 +2,13 @@
 
 
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
+import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
 import com.amazonaws.serverless.proxy.spring.staticapp.LambdaHandler;
 
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import org.junit.jupiter.api.Test;
 import org.springframework.http.HttpHeaders;
 
@@ -23,7 +24,7 @@ public class StaticAppProxyTest {
 
     @Test
     void staticPage() {
-        AwsProxyRequest req = new AwsProxyRequestBuilder("/sample/page", "GET").build();
+        APIGatewayProxyRequestEvent req = new AwsProxyRequestBuilder("/sample/page", "GET").build();
         // we temporarily allow the container to read from any path
         LambdaContainerHandler.getContainerConfig().addValidFilePath("/");
         AwsProxyResponse resp = lambdaHandler.handleRequest(req, new MockLambdaContext());
@@ -31,6 +32,6 @@ void staticPage() {
         assertEquals(200, resp.getStatusCode());
         assertTrue(resp.getBody().startsWith(""));
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
-        assertEquals(MediaType.TEXT_HTML, resp.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE));
+        assertEquals(MediaType.TEXT_HTML, AwsHttpServletRequestHelper.getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE));
     }
 }
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/echoapp/EchoResource.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/echoapp/EchoResource.java
index a6dd033a0..2699a5b7d 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/echoapp/EchoResource.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/echoapp/EchoResource.java
@@ -1,9 +1,9 @@
 package com.amazonaws.serverless.proxy.spring.echoapp;
 
 import com.amazonaws.serverless.proxy.RequestReader;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequestContext;
 import com.amazonaws.serverless.proxy.spring.echoapp.model.MapResponseModel;
 import com.amazonaws.serverless.proxy.spring.echoapp.model.SingleValueModel;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.http.HttpStatus;
@@ -101,10 +101,9 @@ public SingleValueModel echoListQueryString(@RequestParam(value="list") List exten
      * @return An initialized instance of the `SpringLambdaContainerHandler`
      * @throws ContainerInitializationException When the Spring framework fails to start.
      */
-    public static SpringLambdaContainerHandler getAwsProxyHandler(Class... config) throws ContainerInitializationException {
-        return new CustomSpringProxyHandlerBuilder()
+    public static SpringLambdaContainerHandler getAwsProxyHandler(Class... config) throws ContainerInitializationException {
+        return new CustomSpringProxyHandlerBuilder()
                 .defaultProxy()
                 .initializationWrapper(new InitializationWrapper())
                 .configurationClasses(config)
@@ -35,9 +35,9 @@ public static SpringLambdaContainerHandler ge
      * @return An initialized instance of the `SpringLambdaContainerHandler`
      * @throws ContainerInitializationException When the Spring framework fails to start.
      */
-    public static SpringLambdaContainerHandler getAwsProxyHandler(ConfigurableWebApplicationContext applicationContext, String... profiles)
+    public static SpringLambdaContainerHandler getAwsProxyHandler(ConfigurableWebApplicationContext applicationContext, String... profiles)
             throws ContainerInitializationException {
-        return new CustomSpringProxyHandlerBuilder()
+        return new CustomSpringProxyHandlerBuilder()
                 .defaultProxy()
                 .initializationWrapper(new InitializationWrapper())
                 .springApplicationContext(applicationContext)
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/extensibility/StreamLambdaHandler.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/extensibility/StreamLambdaHandler.java
index afc74869d..1e3c2066b 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/extensibility/StreamLambdaHandler.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/extensibility/StreamLambdaHandler.java
@@ -2,11 +2,11 @@
 
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import org.springframework.web.context.support.GenericWebApplicationContext;
 
 import java.io.IOException;
@@ -15,7 +15,7 @@
 
 
 public class StreamLambdaHandler implements RequestStreamHandler {
-    private static final SpringLambdaContainerHandler handler;
+    private static final SpringLambdaContainerHandler handler;
     static {
         try {
             handler = CustomSpringLambdaContainerHandler.getAwsProxyHandler(new GenericWebApplicationContext());
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/profile/SpringProfileTest.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/profile/SpringProfileTest.java
index 7742db7f6..cfde9ebee 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/profile/SpringProfileTest.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/profile/SpringProfileTest.java
@@ -1,6 +1,5 @@
 package com.amazonaws.serverless.proxy.spring.profile;
 
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsServletContext;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
@@ -8,6 +7,7 @@
 import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler;
 import com.amazonaws.serverless.proxy.spring.echoapp.EchoSpringAppConfig;
 import com.amazonaws.serverless.proxy.spring.echoapp.model.MapResponseModel;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -39,10 +39,10 @@ public void clearServletContextCache() {
 
     @Test
     void profile_defaultProfile() throws Exception {
-        AwsProxyRequest request = new AwsProxyRequestBuilder("/profile/spring-properties", "GET")
+        APIGatewayProxyRequestEvent request = new AwsProxyRequestBuilder("/profile/spring-properties", "GET")
                 .build();
 
-        SpringLambdaContainerHandler handler = SpringLambdaContainerHandler.getAwsProxyHandler(EchoSpringAppConfig.class);
+        SpringLambdaContainerHandler handler = SpringLambdaContainerHandler.getAwsProxyHandler(EchoSpringAppConfig.class);
         AwsProxyResponse output = handler.proxy(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
 
@@ -55,9 +55,9 @@ void profile_defaultProfile() throws Exception {
 
     @Test
     void profile_overrideProfile() throws Exception {
-        AwsProxyRequest request = new AwsProxyRequestBuilder("/profile/spring-properties", "GET")
+        APIGatewayProxyRequestEvent request = new AwsProxyRequestBuilder("/profile/spring-properties", "GET")
                 .build();
-        SpringLambdaContainerHandler handler = SpringLambdaContainerHandler.getAwsProxyHandler(EchoSpringAppConfig.class);
+        SpringLambdaContainerHandler handler = SpringLambdaContainerHandler.getAwsProxyHandler(EchoSpringAppConfig.class);
         handler.activateSpringProfiles("override");
         AwsProxyResponse output = handler.proxy(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/springslowapp/LambdaHandler.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/springslowapp/LambdaHandler.java
index 935954d0f..8b27c9c4f 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/springslowapp/LambdaHandler.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/springslowapp/LambdaHandler.java
@@ -1,22 +1,22 @@
 package com.amazonaws.serverless.proxy.spring.springslowapp;
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler;
 import com.amazonaws.serverless.proxy.spring.SpringProxyHandlerBuilder;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestHandler;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 
 import java.time.Instant;
 
-public class LambdaHandler implements RequestHandler {
-    private SpringLambdaContainerHandler handler;
+public class LambdaHandler implements RequestHandler {
+    private SpringLambdaContainerHandler handler;
     private long constructorTime;
 
     public LambdaHandler() throws ContainerInitializationException {
         long startTime = Instant.now().toEpochMilli();
-        handler = new SpringProxyHandlerBuilder()
+        handler = new SpringProxyHandlerBuilder()
                 .defaultProxy()
                 .asyncInit()
                 .configurationClasses(SlowAppConfig.class)
@@ -29,7 +29,7 @@ public long getConstructorTime() {
     }
 
     @Override
-    public AwsProxyResponse handleRequest(AwsProxyRequest awsProxyRequest, Context context) {
+    public AwsProxyResponse handleRequest(APIGatewayProxyRequestEvent awsProxyRequest, Context context) {
         return handler.proxy(awsProxyRequest, context);
     }
 }
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/staticapp/LambdaHandler.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/staticapp/LambdaHandler.java
index ef74e3409..c565053d3 100755
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/staticapp/LambdaHandler.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/staticapp/LambdaHandler.java
@@ -2,22 +2,22 @@
 
 
  import com.amazonaws.serverless.exceptions.ContainerInitializationException;
- import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
  import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
  import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler;
  import com.amazonaws.services.lambda.runtime.Context;
  import com.amazonaws.services.lambda.runtime.RequestHandler;
 
+ import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
  import org.springframework.web.context.support.XmlWebApplicationContext;
 
 
  public class LambdaHandler
-   implements RequestHandler
+   implements RequestHandler
  {
-   SpringLambdaContainerHandler handler;
+   SpringLambdaContainerHandler handler;
    boolean isinitialized = false;
  
-   public AwsProxyResponse handleRequest(AwsProxyRequest awsProxyRequest, Context context)
+   public AwsProxyResponse handleRequest(APIGatewayProxyRequestEvent awsProxyRequest, Context context)
    {
         if (!isinitialized) {
             isinitialized = true;
diff --git a/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootLambdaContainerHandler.java b/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootLambdaContainerHandler.java
index bcd93d01b..15c81b9a5 100644
--- a/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootLambdaContainerHandler.java
+++ b/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootLambdaContainerHandler.java
@@ -16,11 +16,11 @@
 import com.amazonaws.serverless.proxy.*;
 import com.amazonaws.serverless.proxy.internal.servlet.*;
 import com.amazonaws.serverless.proxy.internal.testutils.Timer;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.embedded.ServerlessReactiveServletEmbeddedServerFactory;
 import com.amazonaws.serverless.proxy.spring.embedded.ServerlessServletEmbeddedServerFactory;
 import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -76,9 +76,9 @@ public static SpringBootLambdaContainerHandler getInstance() {
      * @return An initialized instance of the `SpringLambdaContainerHandler`
      * @throws ContainerInitializationException If an error occurs while initializing the Spring framework
      */
-    public static SpringBootLambdaContainerHandler getAwsProxyHandler(Class springBootInitializer, String... profiles)
+    public static SpringBootLambdaContainerHandler getAwsProxyHandler(Class springBootInitializer, String... profiles)
             throws ContainerInitializationException {
-        return new SpringBootProxyHandlerBuilder()
+        return new SpringBootProxyHandlerBuilder()
                 .defaultProxy()
                 .initializationWrapper(new InitializationWrapper())
                 .springBootApplication(springBootInitializer)
diff --git a/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringDelegatingLambdaContainerHandler.java b/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringDelegatingLambdaContainerHandler.java
index 6e267b284..75e8dff81 100644
--- a/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringDelegatingLambdaContainerHandler.java
+++ b/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringDelegatingLambdaContainerHandler.java
@@ -8,6 +8,8 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent;
 import org.springframework.cloud.function.serverless.web.FunctionClassUtils;
 import org.springframework.cloud.function.serverless.web.ProxyHttpServletRequest;
@@ -20,7 +22,6 @@
 import com.amazonaws.serverless.proxy.SecurityContextWriter;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletResponse;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletResponseWriter;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -92,7 +93,7 @@ public void handleRequest(InputStream input, OutputStream output, Context lambda
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
     private HttpServletRequest generateRequest(Map request, Context lambdaContext, SecurityContextWriter securityWriter) {
-        AwsProxyRequest v1Request = this.mapper.convertValue(request, AwsProxyRequest.class);
+        APIGatewayProxyRequestEvent v1Request = this.mapper.convertValue(request, APIGatewayProxyRequestEvent.class);
 
         ProxyHttpServletRequest httpRequest = new ProxyHttpServletRequest(this.mvc.getApplicationContext().getServletContext(),
                 v1Request.getHttpMethod(), v1Request.getPath());
@@ -104,7 +105,7 @@ private HttpServletRequest generateRequest(Map request, Context lambdaContext, S
         httpRequest.setAttribute(RequestReader.API_GATEWAY_CONTEXT_PROPERTY, v1Request.getRequestContext());
         httpRequest.setAttribute(RequestReader.API_GATEWAY_STAGE_VARS_PROPERTY, v1Request.getStageVariables());
         httpRequest.setAttribute(RequestReader.API_GATEWAY_EVENT_PROPERTY, v1Request);
-        httpRequest.setAttribute(RequestReader.ALB_CONTEXT_PROPERTY, v1Request.getRequestContext().getElb());
+        //httpRequest.setAttribute(RequestReader.ALB_CONTEXT_PROPERTY, v1Request.getRequestContext().getElb());
         httpRequest.setAttribute(RequestReader.LAMBDA_CONTEXT_PROPERTY, lambdaContext);
         httpRequest.setAttribute(RequestReader.JAX_SECURITY_CONTEXT_PROPERTY, securityWriter.writeSecurityContext(v1Request, lambdaContext));
         return httpRequest;
@@ -127,4 +128,20 @@ public HttpServletRequest generateRequest2(Map request, Context lambdaContext, S
         httpRequest.setAttribute(RequestReader.JAX_SECURITY_CONTEXT_PROPERTY, securityWriter.writeSecurityContext(v2Request, lambdaContext));
         return httpRequest;
     }
+
+    public HttpServletRequest generateAlbRequest(Map request, Context lambdaContext, SecurityContextWriter securityWriter) {
+        ApplicationLoadBalancerRequestEvent albRequest = this.mapper.convertValue(request, ApplicationLoadBalancerRequestEvent.class);
+        ProxyHttpServletRequest httpRequest = new ProxyHttpServletRequest(this.mvc.getApplicationContext().getServletContext(),
+                albRequest.getHttpMethod(), albRequest.getPath());
+
+        if (StringUtils.hasText(albRequest.getBody())) {
+            httpRequest.setContentType("application/json");
+            httpRequest.setContent(albRequest.getBody().getBytes(StandardCharsets.UTF_8));
+        }
+        httpRequest.setAttribute(RequestReader.ALB_CONTEXT_PROPERTY, albRequest.getRequestContext());
+        httpRequest.setAttribute(RequestReader.ALB_EVENT_PROPERTY, albRequest);
+        httpRequest.setAttribute(RequestReader.LAMBDA_CONTEXT_PROPERTY, lambdaContext);
+        httpRequest.setAttribute(RequestReader.JAX_SECURITY_CONTEXT_PROPERTY, securityWriter.writeSecurityContext(albRequest, lambdaContext));
+        return httpRequest;
+    }
 }
diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/SecurityAppTest.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/SecurityAppTest.java
index d0b579509..58ecdb919 100644
--- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/SecurityAppTest.java
+++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/SecurityAppTest.java
@@ -2,10 +2,10 @@
 
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.securityapp.LambdaHandler;
 import com.amazonaws.serverless.proxy.spring.securityapp.SecurityConfig;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import org.junit.jupiter.api.Test;
 
 import jakarta.ws.rs.core.HttpHeaders;
@@ -25,7 +25,7 @@ public SecurityAppTest() {
 
     @Test
     void helloRequest_withAuth_respondsWithSingleMessage() {
-        AwsProxyRequest req = new AwsProxyRequestBuilder("/hello", "GET").build();
+        APIGatewayProxyRequestEvent req = new AwsProxyRequestBuilder("/hello", "GET").build();
         AwsProxyResponse resp = handler.handleRequest(req, lambdaContext);
         assertEquals(401, resp.getStatusCode());
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.WWW_AUTHENTICATE));
diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/ServletAppTest.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/ServletAppTest.java
index b87c80ce6..657e86b10 100644
--- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/ServletAppTest.java
+++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/ServletAppTest.java
@@ -166,7 +166,7 @@ void stream_getUtf8String_returnsValidUtf8String(String reqType) throws IOExcept
         InputStream req = null;
         switch (type) {
             case "ALB":
-                req = reqBuilder.alb().buildStream();
+                req = reqBuilder.toAlbRequestStream();
                 break;
             case "API_GW":
                 req = reqBuilder.buildStream();
@@ -192,7 +192,7 @@ void stream_getUtf8Json_returnsValidUtf8String(String reqType) throws IOExceptio
         InputStream req = null;
         switch (type) {
             case "ALB":
-                req = reqBuilder.alb().buildStream();
+                req = reqBuilder.toAlbRequestStream();
                 break;
             case "API_GW":
                 req = reqBuilder.buildStream();
diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/SlowAppTest.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/SlowAppTest.java
index f5e83e85e..a21486e9c 100644
--- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/SlowAppTest.java
+++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/SlowAppTest.java
@@ -2,11 +2,11 @@
 
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.slowapp.LambdaHandler;
 import com.amazonaws.serverless.proxy.spring.slowapp.MessageController;
 import com.amazonaws.serverless.proxy.spring.slowapp.SlowTestApplication;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import org.junit.jupiter.api.Test;
 
 import java.time.Instant;
@@ -21,7 +21,7 @@ void slowAppInit_continuesInBackgroundThread_returnsCorrect() {
         LambdaHandler slowApp = new LambdaHandler();
         System.out.println("Start time: " + slowApp.getConstructorTime());
         assertTrue(slowApp.getConstructorTime() < 10_000);
-        AwsProxyRequest req = new AwsProxyRequestBuilder("/hello", "GET").build();
+        APIGatewayProxyRequestEvent req = new AwsProxyRequestBuilder("/hello", "GET").build();
         long startRequestTime = Instant.now().toEpochMilli();
         AwsProxyResponse resp = slowApp.handleRequest(req, new MockLambdaContext());
         long endRequestTime = Instant.now().toEpochMilli();
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 dabc30e24..f805cc069 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
@@ -94,7 +94,7 @@ public class SpringDelegatingLambdaContainerHandlerTests {
             + "            \"accessKey\": null,\n"
             + "            \"accountId\": null,\n"
             + "            \"caller\": null,\n"
-            + "            \"cognitoAmr\": null,\n"
+           // + "            \"cognitoAmr\": null,\n"
             + "            \"cognitoAuthenticationProvider\": null,\n"
             + "            \"cognitoAuthenticationType\": null,\n"
             + "            \"cognitoIdentityId\": null,\n"
diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/WebFluxAppTest.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/WebFluxAppTest.java
index 0a477ef0f..869e9c3ec 100644
--- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/WebFluxAppTest.java
+++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/WebFluxAppTest.java
@@ -1,9 +1,7 @@
 package com.amazonaws.serverless.proxy.spring;
 
-import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.webfluxapp.LambdaHandler;
 import com.amazonaws.serverless.proxy.spring.webfluxapp.MessageController;
diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/embedded/ServerlessServletEmbeddedServerFactoryTest.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/embedded/ServerlessServletEmbeddedServerFactoryTest.java
index 5ffd4a311..e60ebf0c6 100644
--- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/embedded/ServerlessServletEmbeddedServerFactoryTest.java
+++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/embedded/ServerlessServletEmbeddedServerFactoryTest.java
@@ -6,9 +6,9 @@
 import com.amazonaws.serverless.proxy.InitializationWrapper;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletRequestReader;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletResponseWriter;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.WebApplicationType;
 import org.springframework.boot.web.servlet.ServletContextInitializer;
@@ -19,8 +19,8 @@
 import static org.junit.jupiter.api.Assertions.fail;
 
 public class ServerlessServletEmbeddedServerFactoryTest {
-    private SpringBootLambdaContainerHandler handler = new SpringBootLambdaContainerHandler<>(
-            AwsProxyRequest.class,
+    private SpringBootLambdaContainerHandler handler = new SpringBootLambdaContainerHandler<>(
+            APIGatewayProxyRequestEvent.class,
             AwsProxyResponse.class,
             new AwsProxyHttpServletRequestReader(),
             new AwsProxyHttpServletResponseWriter(),
diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/securityapp/LambdaHandler.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/securityapp/LambdaHandler.java
index ae8ba21ac..dd41c4307 100644
--- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/securityapp/LambdaHandler.java
+++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/securityapp/LambdaHandler.java
@@ -1,14 +1,14 @@
 package com.amazonaws.serverless.proxy.spring.securityapp;
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestHandler;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 
-public class LambdaHandler implements RequestHandler {
-    private static SpringBootLambdaContainerHandler handler;
+public class LambdaHandler implements RequestHandler {
+    private static SpringBootLambdaContainerHandler handler;
 
     static {
         try {
@@ -19,7 +19,7 @@ public class LambdaHandler implements RequestHandler {
-    private static SpringBootLambdaContainerHandler handler;
+    private static SpringBootLambdaContainerHandler handler;
     private static SpringBootLambdaContainerHandler httpApiHandler;
+    private static SpringBootLambdaContainerHandler albHandler;
     private String type;
 
     public LambdaHandler(String reqType) {
@@ -22,14 +23,21 @@ public LambdaHandler(String reqType) {
         try {
             switch (type) {
                 case "API_GW":
-                case "ALB":
-                    handler = new SpringBootProxyHandlerBuilder()
+                    handler = new SpringBootProxyHandlerBuilder()
                                 .defaultProxy()
                                 .initializationWrapper(new InitializationWrapper())
                                 .servletApplication()
                                 .springBootApplication(ServletApplication.class)
                                 .buildAndInitialize();
                     break;
+                case "ALB":
+                    albHandler = new SpringBootProxyHandlerBuilder()
+                            .defaultAlbProxy()
+                            .initializationWrapper(new InitializationWrapper())
+                            .servletApplication()
+                            .springBootApplication(ServletApplication.class)
+                            .buildAndInitialize();
+                    break;
                 case "HTTP_API":
                     httpApiHandler = new SpringBootProxyHandlerBuilder()
                             .defaultHttpApiV2Proxy()
@@ -50,7 +58,7 @@ public AwsProxyResponse handleRequest(AwsProxyRequestBuilder awsProxyRequest, Co
             case "API_GW":
                 return handler.proxy(awsProxyRequest.build(), context);
             case "ALB":
-                return handler.proxy(awsProxyRequest.alb().build(), context);
+                return albHandler.proxy(awsProxyRequest.toAlbRequest(), context);
             case "HTTP_API":
                 return httpApiHandler.proxy(awsProxyRequest.toHttpApiV2Request(), context);
             default:
diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/servletapp/LambdaStreamHandler.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/servletapp/LambdaStreamHandler.java
index d2a3c8d0c..a826100a2 100644
--- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/servletapp/LambdaStreamHandler.java
+++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/servletapp/LambdaStreamHandler.java
@@ -2,21 +2,25 @@
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.InitializationWrapper;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
+import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
 import com.amazonaws.serverless.proxy.spring.SpringBootProxyHandlerBuilder;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
+import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent;
+import jakarta.ws.rs.core.HttpHeaders;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
 public class LambdaStreamHandler implements RequestStreamHandler {
-    private static SpringBootLambdaContainerHandler handler;
+    private static SpringBootLambdaContainerHandler handler;
     private static SpringBootLambdaContainerHandler httpApiHandler;
+    private static SpringBootLambdaContainerHandler albHandler;
     private String type;
 
     public LambdaStreamHandler(String reqType) {
@@ -24,13 +28,20 @@ public LambdaStreamHandler(String reqType) {
         try {
             switch (type) {
                 case "API_GW":
+                    handler = new SpringBootProxyHandlerBuilder()
+                            .defaultProxy()
+                            .initializationWrapper(new InitializationWrapper())
+                            .servletApplication()
+                            .springBootApplication(ServletApplication.class)
+                            .buildAndInitialize();
+                    break;
                 case "ALB":
-                    handler = new SpringBootProxyHandlerBuilder()
-                                .defaultProxy()
-                                .initializationWrapper(new InitializationWrapper())
-                                .servletApplication()
-                                .springBootApplication(ServletApplication.class)
-                                .buildAndInitialize();
+                    albHandler = new SpringBootProxyHandlerBuilder()
+                            .defaultAlbProxy()
+                            .initializationWrapper(new InitializationWrapper())
+                            .servletApplication()
+                            .springBootApplication(ServletApplication.class)
+                            .buildAndInitialize();
                     break;
                 case "HTTP_API":
                     httpApiHandler = new SpringBootProxyHandlerBuilder()
@@ -50,9 +61,11 @@ public LambdaStreamHandler(String reqType) {
     public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
         switch (type) {
             case "API_GW":
-            case "ALB":
                 handler.proxyStream(inputStream, outputStream, context);
                 break;
+            case "ALB":
+                albHandler.proxyStream(inputStream, outputStream, context);
+                break;
             case "HTTP_API":
                 httpApiHandler.proxyStream(inputStream, outputStream, context);
         }
diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/slowapp/LambdaHandler.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/slowapp/LambdaHandler.java
index ec6993a7d..748574e37 100644
--- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/slowapp/LambdaHandler.java
+++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/slowapp/LambdaHandler.java
@@ -1,25 +1,24 @@
 package com.amazonaws.serverless.proxy.spring.slowapp;
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
-import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletRequest;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
 import com.amazonaws.serverless.proxy.spring.SpringBootProxyHandlerBuilder;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestHandler;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 
 import java.time.Instant;
 
-public class LambdaHandler implements RequestHandler {
-    private SpringBootLambdaContainerHandler handler;
+public class LambdaHandler implements RequestHandler {
+    private SpringBootLambdaContainerHandler handler;
     private long constructorTime;
 
     public LambdaHandler() {
         try {
             long startTime = Instant.now().toEpochMilli();
             System.out.println("startCall: " + startTime);
-            handler = new SpringBootProxyHandlerBuilder()
+            handler = new SpringBootProxyHandlerBuilder()
                     .defaultProxy()
                     .asyncInit()
                     .springBootApplication(SlowTestApplication.class)
@@ -35,7 +34,7 @@ public long getConstructorTime() {
     }
 
     @Override
-    public AwsProxyResponse handleRequest(AwsProxyRequest awsProxyRequest, Context context) {
+    public AwsProxyResponse handleRequest(APIGatewayProxyRequestEvent awsProxyRequest, Context context) {
         return handler.proxy(awsProxyRequest, context);
     }
 }
diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/webfluxapp/LambdaHandler.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/webfluxapp/LambdaHandler.java
index 7ea1d6e09..28c232794 100644
--- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/webfluxapp/LambdaHandler.java
+++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/webfluxapp/LambdaHandler.java
@@ -3,17 +3,19 @@
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.InitializationWrapper;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
 import com.amazonaws.serverless.proxy.spring.SpringBootProxyHandlerBuilder;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestHandler;
+import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent;
 
 public class LambdaHandler implements RequestHandler {
-    private static SpringBootLambdaContainerHandler handler;
+    private static SpringBootLambdaContainerHandler handler;
     private static SpringBootLambdaContainerHandler httpApiHandler;
+    private static SpringBootLambdaContainerHandler albHandler;
 
     private String type;
 
@@ -22,13 +24,19 @@ public LambdaHandler(String reqType) {
         try {
             switch (type) {
                 case "API_GW":
-                case "ALB":
-                    handler = new SpringBootProxyHandlerBuilder()
+                    handler = new SpringBootProxyHandlerBuilder()
                             .defaultProxy()
                             .initializationWrapper(new InitializationWrapper())
                             .springBootApplication(WebFluxTestApplication.class)
                             .buildAndInitialize();
                     break;
+                case "ALB":
+                    albHandler = new SpringBootProxyHandlerBuilder()
+                            .defaultAlbProxy()
+                            .initializationWrapper(new InitializationWrapper())
+                            .springBootApplication(WebFluxTestApplication.class)
+                            .buildAndInitialize();
+                    break;
                 case "HTTP_API":
                     httpApiHandler = new SpringBootProxyHandlerBuilder()
                             .defaultHttpApiV2Proxy()
@@ -48,7 +56,7 @@ public AwsProxyResponse handleRequest(AwsProxyRequestBuilder awsProxyRequest, Co
             case "API_GW":
                 return handler.proxy(awsProxyRequest.build(), context);
             case "ALB":
-                return handler.proxy(awsProxyRequest.alb().build(), context);
+                return albHandler.proxy(awsProxyRequest.toAlbRequest(), context);
             case "HTTP_API":
                 return httpApiHandler.proxy(awsProxyRequest.toHttpApiV2Request(), context);
             default:
diff --git a/aws-serverless-jersey-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java b/aws-serverless-jersey-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java
index 768a5ad98..2f19a0282 100644
--- a/aws-serverless-jersey-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java
+++ b/aws-serverless-jersey-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java
@@ -1,7 +1,7 @@
 package ${groupId};
 
 import com.amazonaws.serverless.proxy.jersey.JerseyLambdaContainerHandler;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
@@ -28,7 +28,7 @@ public class StreamLambdaHandler implements RequestStreamHandler {
             .property(ServerProperties.MOXY_JSON_FEATURE_DISABLE,true)
             .register(PingResource.class)
             .register(JacksonFeature.class);
-    private static final JerseyLambdaContainerHandler handler
+    private static final JerseyLambdaContainerHandler handler
             = JerseyLambdaContainerHandler.getAwsProxyHandler(jerseyApplication);
 
     @Override
diff --git a/aws-serverless-jersey-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java b/aws-serverless-jersey-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
index 53db9b161..ece4f6b37 100644
--- a/aws-serverless-jersey-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
+++ b/aws-serverless-jersey-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
@@ -2,6 +2,7 @@
 
 
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
+import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
@@ -52,7 +53,7 @@ public void ping_streamRequest_respondsWithHello() {
         assertTrue(response.getBody().contains("Hello, World!"));
 
         assertTrue(response.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
-        assertTrue(response.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE).startsWith(MediaType.APPLICATION_JSON));
+        assertTrue(AwsHttpServletRequestHelper.getFirst(response.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE).startsWith(MediaType.APPLICATION_JSON));
     }
 
     @Test
diff --git a/aws-serverless-spring-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java b/aws-serverless-spring-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java
index 8cddb47e9..174de1338 100644
--- a/aws-serverless-spring-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java
+++ b/aws-serverless-spring-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java
@@ -2,7 +2,7 @@
 
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler;
 import com.amazonaws.services.lambda.runtime.Context;
@@ -14,7 +14,7 @@
 
 
 public class StreamLambdaHandler implements RequestStreamHandler {
-    private static SpringLambdaContainerHandler handler;
+    private static SpringLambdaContainerHandler handler;
     static {
         try {
             handler = SpringLambdaContainerHandler.getAwsProxyHandler(SpringApiConfig.class);
diff --git a/aws-serverless-spring-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java b/aws-serverless-spring-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
index 53db9b161..ece4f6b37 100644
--- a/aws-serverless-spring-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
+++ b/aws-serverless-spring-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
@@ -2,6 +2,7 @@
 
 
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
+import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
@@ -52,7 +53,7 @@ public void ping_streamRequest_respondsWithHello() {
         assertTrue(response.getBody().contains("Hello, World!"));
 
         assertTrue(response.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
-        assertTrue(response.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE).startsWith(MediaType.APPLICATION_JSON));
+        assertTrue(AwsHttpServletRequestHelper.getFirst(response.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE).startsWith(MediaType.APPLICATION_JSON));
     }
 
     @Test
diff --git a/aws-serverless-springboot3-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java b/aws-serverless-springboot3-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java
index dca9650d3..e7a93a049 100644
--- a/aws-serverless-springboot3-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java
+++ b/aws-serverless-springboot3-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java
@@ -2,7 +2,7 @@
 
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
 import com.amazonaws.services.lambda.runtime.Context;
@@ -14,7 +14,7 @@
 
 
 public class StreamLambdaHandler implements RequestStreamHandler {
-    private static SpringBootLambdaContainerHandler handler;
+    private static SpringBootLambdaContainerHandler handler;
     static {
         try {
             handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class);
diff --git a/aws-serverless-springboot3-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java b/aws-serverless-springboot3-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
index 26d5360bf..db68a3fe7 100644
--- a/aws-serverless-springboot3-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
+++ b/aws-serverless-springboot3-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
@@ -2,6 +2,7 @@
 
 
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
+import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
 import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
@@ -51,7 +52,7 @@ public void ping_streamRequest_respondsWithHello() {
         assertTrue(response.getBody().contains("Hello, World!"));
 
         assertTrue(response.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
-        assertTrue(response.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE).startsWith(MediaType.APPLICATION_JSON));
+        assertTrue(AwsHttpServletRequestHelper.getFirst(response.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE).startsWith(MediaType.APPLICATION_JSON));
     }
 
     @Test
diff --git a/pom.xml b/pom.xml
index 450e01945..2ab862554 100644
--- a/pom.xml
+++ b/pom.xml
@@ -76,7 +76,7 @@
     
 
     
-        0.7
+        0.6
         8.4.0
         2.15.2
         2.0.9
@@ -279,6 +279,20 @@
                     org.jacoco
                     jacoco-maven-plugin
                     0.8.10
+                    
+                        
+                            
+                                prepare-agent
+                            
+                        
+                        
+                            report
+                            prepare-package
+                            
+                                report
+                            
+                        
+                    
                 
             
         

From 905aa4bfd5a297d66ebab14c070789fe3f14181f Mon Sep 17 00:00:00 2001
From: mbfreder 
Date: Sat, 14 Oct 2023 20:40:50 -0700
Subject: [PATCH 5/8] Migrated to AwsProxyResponseEvent

---
 .../proxy/AwsAlbExceptionHandler.java         |  10 +-
 .../proxy/AwsProxyExceptionHandler.java       |  18 ++-
 .../servlet/AwsAlbHttpServletRequest.java     |   2 +-
 .../AwsAlbHttpServletResponseWriter.java      |  10 +-
 .../AwsProxyHttpServletResponseWriter.java    |  10 +-
 .../ServletLambdaContainerHandlerBuilder.java |   8 +-
 .../proxy/model/AwsProxyResponse.java         | 142 ------------------
 .../proxy/AwsAlbExceptionHandlerTest.java     |  34 ++---
 .../proxy/AwsProxyExceptionHandlerTest.java   |  34 ++---
 .../internal/LambdaContainerHandlerTest.java  |  10 +-
 .../AwsAlbHttpServletResponseWriterTest.java  |  10 +-
 .../internal/servlet/AwsAsyncContextTest.java |   6 +-
 .../AwsProxyRequestDispatcherTest.java        |  12 +-
 ...vletLambdaContainerHandlerBuilderTest.java |  10 +-
 .../jersey/JerseyLambdaContainerHandler.java  |  20 +--
 .../proxy/jersey/JerseyAwsProxyTest.java      |  64 ++++----
 .../proxy/jersey/JerseyInjectionTest.java     |   4 +-
 .../proxy/jersey/JerseyParamEncodingTest.java |  38 ++---
 .../spring/SpringLambdaContainerHandler.java  |  10 +-
 .../spring/SpringProxyHandlerBuilder.java     |  16 +-
 .../serverless/proxy/spring/SlowAppTest.java  |   4 +-
 .../proxy/spring/SpringAwsProxyTest.java      |  65 ++++----
 .../spring/SpringServletContextTest.java      |  16 +-
 .../proxy/spring/StaticAppProxyTest.java      |   4 +-
 .../CustomSpringLambdaContainerHandler.java   |   8 +-
 .../CustomSpringProxyHandlerBuilder.java      |   4 +-
 .../extensibility/StreamLambdaHandler.java    |   4 +-
 .../spring/profile/SpringProfileTest.java     |  10 +-
 .../spring/springslowapp/LambdaHandler.java   |   8 +-
 .../proxy/spring/staticapp/LambdaHandler.java |  10 +-
 .../SpringBootLambdaContainerHandler.java     |   6 +-
 .../spring/SpringBootProxyHandlerBuilder.java |  14 +-
 .../proxy/spring/SecurityAppTest.java         |   4 +-
 .../proxy/spring/ServletAppTest.java          |  26 ++--
 .../serverless/proxy/spring/SlowAppTest.java  |   4 +-
 .../proxy/spring/WebFluxAppTest.java          |   8 +-
 ...rlessServletEmbeddedServerFactoryTest.java |   6 +-
 .../spring/securityapp/LambdaHandler.java     |   8 +-
 .../spring/servletapp/LambdaHandler.java      |  12 +-
 .../servletapp/LambdaStreamHandler.java       |   8 +-
 .../proxy/spring/slowapp/LambdaHandler.java   |   8 +-
 .../spring/webfluxapp/LambdaHandler.java      |  12 +-
 .../src/main/java/StreamLambdaHandler.java    |   4 +-
 .../test/java/StreamLambdaHandlerTest.java    |  12 +-
 .../src/main/java/StreamLambdaHandler.java    |   4 +-
 .../test/java/StreamLambdaHandlerTest.java    |  12 +-
 .../src/main/java/StreamLambdaHandler.java    |   4 +-
 .../test/java/StreamLambdaHandlerTest.java    |  12 +-
 pom.xml                                       |   2 +-
 49 files changed, 316 insertions(+), 451 deletions(-)
 delete mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AwsProxyResponse.java

diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/AwsAlbExceptionHandler.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/AwsAlbExceptionHandler.java
index 9420e0d32..b5c1e32ce 100644
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/AwsAlbExceptionHandler.java
+++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/AwsAlbExceptionHandler.java
@@ -2,8 +2,8 @@
 
 import com.amazonaws.serverless.exceptions.InvalidRequestEventException;
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.model.ErrorModel;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import jakarta.ws.rs.InternalServerErrorException;
 import jakarta.ws.rs.core.HttpHeaders;
@@ -18,7 +18,7 @@
 import java.util.List;
 import java.util.Map;
 
-public class AwsAlbExceptionHandler implements ExceptionHandler{
+public class AwsAlbExceptionHandler implements ExceptionHandler{
 
     private Logger log = LoggerFactory.getLogger(AwsAlbExceptionHandler.class);
 
@@ -46,13 +46,13 @@ public class AwsAlbExceptionHandler implements ExceptionHandler {
+        implements ExceptionHandler {
 
     private Logger log = LoggerFactory.getLogger(AwsProxyExceptionHandler.class);
 
@@ -72,23 +72,29 @@ public class AwsProxyExceptionHandler
 
 
     @Override
-    public AwsProxyResponse handle(Throwable ex) {
+    public AwsProxyResponseEvent handle(Throwable ex) {
         log.error("Called exception handler for:", ex);
 
         // adding a print stack trace in case we have no appender or we are running inside SAM local, where need the
         // output to go to the stderr.
         ex.printStackTrace();
+        AwsProxyResponseEvent responseEvent = new AwsProxyResponseEvent();
+        responseEvent.setMultiValueHeaders(headers);
         if (ex instanceof InvalidRequestEventException || ex instanceof InternalServerErrorException) {
-            return new AwsProxyResponse(500, headers, getErrorJson(INTERNAL_SERVER_ERROR));
+            responseEvent.setBody(getErrorJson(INTERNAL_SERVER_ERROR));
+            responseEvent.setStatusCode(500);
+            return responseEvent;
         } else {
-            return new AwsProxyResponse(502, headers, getErrorJson(GATEWAY_TIMEOUT_ERROR));
+            responseEvent.setBody(getErrorJson(GATEWAY_TIMEOUT_ERROR));
+            responseEvent.setStatusCode(502);
+            return responseEvent;
         }
     }
 
 
     @Override
     public void handle(Throwable ex, OutputStream stream) throws IOException {
-        AwsProxyResponse response = handle(ex);
+        AwsProxyResponseEvent response = handle(ex);
 
         LambdaContainerHandler.getObjectMapper().writeValue(stream, response);
     }
diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletRequest.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletRequest.java
index 3ff2ee0f3..8319f58cb 100644
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletRequest.java
+++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletRequest.java
@@ -46,7 +46,7 @@ public AwsAlbHttpServletRequest(ApplicationLoadBalancerRequestEvent albRequest,
         this.config = config;
     }
 
-    public ApplicationLoadBalancerRequestEvent getAwsProxyRequest() {
+    public ApplicationLoadBalancerRequestEvent getAlbRequest() {
         return this.request;
     }
     @Override
diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletResponseWriter.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletResponseWriter.java
index 4bbc34bb1..46cf73b23 100644
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletResponseWriter.java
+++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletResponseWriter.java
@@ -4,16 +4,16 @@
 import com.amazonaws.serverless.proxy.ResponseWriter;
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
 import com.amazonaws.serverless.proxy.internal.testutils.Timer;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.model.Headers;
 import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import jakarta.ws.rs.core.Response;
 
 import java.util.Base64;
 import java.util.HashMap;
 import java.util.Map;
 
-public class AwsAlbHttpServletResponseWriter extends ResponseWriter {
+public class AwsAlbHttpServletResponseWriter extends ResponseWriter {
 
     private boolean writeSingleValueHeaders;
 
@@ -26,9 +26,9 @@ public AwsAlbHttpServletResponseWriter(boolean singleValueHeaders) {
     }
 
     @Override
-    public AwsProxyResponse writeResponse(AwsHttpServletResponse containerResponse, Context lambdaContext) throws InvalidResponseObjectException {
+    public AwsProxyResponseEvent writeResponse(AwsHttpServletResponse containerResponse, Context lambdaContext) throws InvalidResponseObjectException {
         Timer.start("SERVLET_RESPONSE_WRITE");
-        AwsProxyResponse awsProxyResponse = new AwsProxyResponse();
+        AwsProxyResponseEvent awsProxyResponse = new AwsProxyResponseEvent();
         if (containerResponse.getAwsResponseBodyString() != null) {
             String responseString;
 
@@ -36,7 +36,7 @@ public AwsProxyResponse writeResponse(AwsHttpServletResponse containerResponse,
                 responseString = containerResponse.getAwsResponseBodyString();
             } else {
                 responseString = Base64.getEncoder().encodeToString(containerResponse.getAwsResponseBodyBytes());
-                awsProxyResponse.setBase64Encoded(true);
+                awsProxyResponse.setIsBase64Encoded(true);
             }
 
             awsProxyResponse.setBody(responseString);
diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletResponseWriter.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletResponseWriter.java
index ddbe1858a..6624ca4ea 100644
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletResponseWriter.java
+++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletResponseWriter.java
@@ -17,9 +17,9 @@
 import com.amazonaws.serverless.proxy.ResponseWriter;
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
 import com.amazonaws.serverless.proxy.internal.testutils.Timer;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.model.Headers;
 import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 
 import java.util.Base64;
 import java.util.HashMap;
@@ -30,7 +30,7 @@
  * Creates an AwsProxyResponse object given an AwsHttpServletResponse object. If the
  * response is not populated with a status code we infer a default 200 status code.
  */
-public class AwsProxyHttpServletResponseWriter extends ResponseWriter {
+public class AwsProxyHttpServletResponseWriter extends ResponseWriter {
 
     private boolean writeSingleValueHeaders;
 
@@ -47,10 +47,10 @@ public AwsProxyHttpServletResponseWriter(boolean singleValueHeaders) {
     //-------------------------------------------------------------
 
     @Override
-    public AwsProxyResponse writeResponse(AwsHttpServletResponse containerResponse, Context lambdaContext)
+    public AwsProxyResponseEvent writeResponse(AwsHttpServletResponse containerResponse, Context lambdaContext)
             throws InvalidResponseObjectException {
         Timer.start("SERVLET_RESPONSE_WRITE");
-        AwsProxyResponse awsProxyResponse = new AwsProxyResponse();
+        AwsProxyResponseEvent awsProxyResponse = new AwsProxyResponseEvent();
         if (containerResponse.getAwsResponseBodyString() != null) {
             String responseString;
 
@@ -58,7 +58,7 @@ public AwsProxyResponse writeResponse(AwsHttpServletResponse containerResponse,
                 responseString = containerResponse.getAwsResponseBodyString();
             } else {
                 responseString = Base64.getEncoder().encodeToString(containerResponse.getAwsResponseBodyBytes());
-                awsProxyResponse.setBase64Encoded(true);
+                awsProxyResponse.setIsBase64Encoded(true);
             }
 
             awsProxyResponse.setBody(responseString);
diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ServletLambdaContainerHandlerBuilder.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ServletLambdaContainerHandlerBuilder.java
index c897ca2d3..0abd318d0 100644
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ServletLambdaContainerHandlerBuilder.java
+++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ServletLambdaContainerHandlerBuilder.java
@@ -14,9 +14,9 @@
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.*;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 
 import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent;
 import jakarta.servlet.http.HttpServletRequest;
@@ -99,7 +99,7 @@ public Builder defaultProxy() {
                 .securityContextWriter((SecurityContextWriter) new AwsProxySecurityContextWriter())
                 .exceptionHandler((ExceptionHandler) new AwsProxyExceptionHandler())
                 .requestTypeClass((Class) APIGatewayProxyRequestEvent.class)
-                .responseTypeClass((Class) AwsProxyResponse.class);
+                .responseTypeClass((Class) AwsProxyResponseEvent.class);
         return self();
     }
 
@@ -110,7 +110,7 @@ public Builder defaultAlbProxy() {
                 .securityContextWriter((SecurityContextWriter) new AwsAlbSecurityContextWriter())
                 .exceptionHandler((ExceptionHandler) new AwsAlbExceptionHandler())
                 .requestTypeClass((Class) ApplicationLoadBalancerRequestEvent.class)
-                .responseTypeClass((Class) AwsProxyResponse.class);
+                .responseTypeClass((Class) AwsProxyResponseEvent.class);
         return self();
     }
 
@@ -126,7 +126,7 @@ public Builder defaultHttpApiV2Proxy() {
                 .securityContextWriter((SecurityContextWriter) new AwsHttpApiV2SecurityContextWriter())
                 .exceptionHandler((ExceptionHandler) new AwsProxyExceptionHandler())
                 .requestTypeClass((Class) APIGatewayV2HTTPEvent.class)
-                .responseTypeClass((Class) AwsProxyResponse.class);
+                .responseTypeClass((Class) AwsProxyResponseEvent.class);
         return self();
 
     }
diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AwsProxyResponse.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AwsProxyResponse.java
deleted file mode 100644
index a23503c70..000000000
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AwsProxyResponse.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
- * with the License. A copy of the License is located at
- *
- * http://aws.amazon.com/apache2.0/
- *
- * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
- * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
- * and limitations under the License.
- */
-package com.amazonaws.serverless.proxy.model;
-
-
-import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import java.util.List;
-import java.util.Map;
-
-
-/**
- * Response object for an API Gateway method using AWS_PROXY integrations
- */
-@JsonInclude(JsonInclude.Include.NON_NULL)
-public class AwsProxyResponse {
-
-    //-------------------------------------------------------------
-    // Variables - Private
-    //-------------------------------------------------------------
-
-    private int statusCode;
-    private String statusDescription;
-    private Map headers;
-    private Map> multiValueHeaders;
-    private String body;
-    private boolean isBase64Encoded;
-
-
-    //-------------------------------------------------------------
-    // Constructors
-    //-------------------------------------------------------------
-
-    public AwsProxyResponse() {
-
-    }
-
-
-    public AwsProxyResponse(int statusCode) {
-        this(statusCode, null);
-    }
-
-
-    public AwsProxyResponse(int statusCode, Headers headers) {
-        this(statusCode, headers, null);
-    }
-
-
-    public AwsProxyResponse(int statusCode, Headers headers, String body) {
-        this.statusCode = statusCode;
-        this.multiValueHeaders = headers;
-        this.body = body;
-    }
-
-
-    //-------------------------------------------------------------
-    // Methods - Public
-    //-------------------------------------------------------------
-
-    public void addHeader(String key, String value) {
-        if (this.multiValueHeaders == null) {
-            this.multiValueHeaders = new Headers();
-        }
-        AwsHttpServletRequestHelper.putSingle(this.multiValueHeaders, key, value);
-        //this.multiValueHeaders.add(key, value);
-    }
-
-
-    //-------------------------------------------------------------
-    // Methods - Getter/Setter
-    //-------------------------------------------------------------
-
-    public int getStatusCode() {
-        return statusCode;
-    }
-
-
-    public void setStatusCode(int statusCode) {
-        this.statusCode = statusCode;
-    }
-
-
-    public Map getHeaders() {
-        return headers;
-    }
-
-
-    public void setHeaders(Map headers) {
-        this.headers = headers;
-    }
-
-
-    public Map> getMultiValueHeaders() {
-        return multiValueHeaders;
-    }
-
-
-    public void setMultiValueHeaders(Map> multiValueHeaders) {
-        this.multiValueHeaders = multiValueHeaders;
-    }
-
-
-    public String getBody() {
-        return body;
-    }
-
-
-    public void setBody(String body) {
-        this.body = body;
-    }
-
-    @JsonProperty("isBase64Encoded")
-    public boolean isBase64Encoded() {
-        return isBase64Encoded;
-    }
-
-    public void setBase64Encoded(boolean base64Encoded) {
-        isBase64Encoded = base64Encoded;
-    }
-
-
-    public String getStatusDescription() {
-        return statusDescription;
-    }
-
-
-    public void setStatusDescription(String statusDescription) {
-        this.statusDescription = statusDescription;
-    }
-}
diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsAlbExceptionHandlerTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsAlbExceptionHandlerTest.java
index 4c48228fc..ec4bab12f 100644
--- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsAlbExceptionHandlerTest.java
+++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsAlbExceptionHandlerTest.java
@@ -2,8 +2,8 @@
 
 import com.amazonaws.serverless.exceptions.InvalidRequestEventException;
 import com.amazonaws.serverless.exceptions.InvalidResponseObjectException;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.model.ErrorModel;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import jakarta.ws.rs.InternalServerErrorException;
@@ -38,7 +38,7 @@ public void setUp() {
 
     @Test
     void typedHandle_InvalidRequestEventException_500State() {
-        AwsProxyResponse resp = exceptionHandler.handle(new InvalidRequestEventException(INVALID_REQUEST_MESSAGE, null));
+        AwsProxyResponseEvent resp = exceptionHandler.handle(new InvalidRequestEventException(INVALID_REQUEST_MESSAGE, null));
 
         assertNotNull(resp);
         assertEquals(500, resp.getStatusCode());
@@ -47,7 +47,7 @@ void typedHandle_InvalidRequestEventException_500State() {
     @Test
     void typedHandle_InvalidRequestEventException_responseString()
             throws JsonProcessingException {
-        AwsProxyResponse resp = exceptionHandler.handle(new InvalidRequestEventException(INVALID_REQUEST_MESSAGE, null));
+        AwsProxyResponseEvent resp = exceptionHandler.handle(new InvalidRequestEventException(INVALID_REQUEST_MESSAGE, null));
 
         assertNotNull(resp);
         String body = objectMapper.writeValueAsString(new ErrorModel(AwsProxyExceptionHandler.INTERNAL_SERVER_ERROR));
@@ -56,7 +56,7 @@ void typedHandle_InvalidRequestEventException_responseString()
 
     @Test
     void typedHandle_InvalidRequestEventException_jsonContentTypeHeader() {
-        AwsProxyResponse resp = exceptionHandler.handle(new InvalidRequestEventException(INVALID_REQUEST_MESSAGE, null));
+        AwsProxyResponseEvent resp = exceptionHandler.handle(new InvalidRequestEventException(INVALID_REQUEST_MESSAGE, null));
 
         assertNotNull(resp);
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
@@ -65,7 +65,7 @@ void typedHandle_InvalidRequestEventException_jsonContentTypeHeader() {
 
     @Test
     void typedHandle_InvalidResponseObjectException_502State() {
-        AwsProxyResponse resp = exceptionHandler.handle(new InvalidResponseObjectException(INVALID_RESPONSE_MESSAGE, null));
+        AwsProxyResponseEvent resp = exceptionHandler.handle(new InvalidResponseObjectException(INVALID_RESPONSE_MESSAGE, null));
 
         assertNotNull(resp);
         assertEquals(502, resp.getStatusCode());
@@ -74,7 +74,7 @@ void typedHandle_InvalidResponseObjectException_502State() {
     @Test
     void typedHandle_InvalidResponseObjectException_responseString()
             throws JsonProcessingException {
-        AwsProxyResponse resp = exceptionHandler.handle(new InvalidResponseObjectException(INVALID_RESPONSE_MESSAGE, null));
+        AwsProxyResponseEvent resp = exceptionHandler.handle(new InvalidResponseObjectException(INVALID_RESPONSE_MESSAGE, null));
 
         assertNotNull(resp);
         String body = objectMapper.writeValueAsString(new ErrorModel(AwsProxyExceptionHandler.GATEWAY_TIMEOUT_ERROR));
@@ -83,7 +83,7 @@ void typedHandle_InvalidResponseObjectException_responseString()
 
     @Test
     void typedHandle_InvalidResponseObjectException_jsonContentTypeHeader() {
-        AwsProxyResponse resp = exceptionHandler.handle(new InvalidResponseObjectException(INVALID_RESPONSE_MESSAGE, null));
+        AwsProxyResponseEvent resp = exceptionHandler.handle(new InvalidResponseObjectException(INVALID_RESPONSE_MESSAGE, null));
 
         assertNotNull(resp);
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
@@ -97,7 +97,7 @@ void typedHandle_InternalServerErrorException_500State() {
         InternalServerErrorException mockInternalServerErrorException = Mockito.mock(InternalServerErrorException.class);
         Mockito.when(mockInternalServerErrorException.getMessage()).thenReturn(INTERNAL_SERVER_ERROR_MESSAGE);
 
-        AwsProxyResponse resp = exceptionHandler.handle(mockInternalServerErrorException);
+        AwsProxyResponseEvent resp = exceptionHandler.handle(mockInternalServerErrorException);
 
         assertNotNull(resp);
         assertEquals(500, resp.getStatusCode());
@@ -109,7 +109,7 @@ void typedHandle_InternalServerErrorException_responseString()
         InternalServerErrorException mockInternalServerErrorException = Mockito.mock(InternalServerErrorException.class);
         Mockito.when(mockInternalServerErrorException.getMessage()).thenReturn(INTERNAL_SERVER_ERROR_MESSAGE);
 
-        AwsProxyResponse resp = exceptionHandler.handle(mockInternalServerErrorException);
+        AwsProxyResponseEvent resp = exceptionHandler.handle(mockInternalServerErrorException);
 
         assertNotNull(resp);
         String body = objectMapper.writeValueAsString(new ErrorModel(AwsProxyExceptionHandler.INTERNAL_SERVER_ERROR));
@@ -121,7 +121,7 @@ void typedHandle_InternalServerErrorException_jsonContentTypeHeader() {
         InternalServerErrorException mockInternalServerErrorException = Mockito.mock(InternalServerErrorException.class);
         Mockito.when(mockInternalServerErrorException.getMessage()).thenReturn(INTERNAL_SERVER_ERROR_MESSAGE);
 
-        AwsProxyResponse resp = exceptionHandler.handle(mockInternalServerErrorException);
+        AwsProxyResponseEvent resp = exceptionHandler.handle(mockInternalServerErrorException);
 
         assertNotNull(resp);
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
@@ -131,7 +131,7 @@ void typedHandle_InternalServerErrorException_jsonContentTypeHeader() {
     @Test
     void typedHandle_NullPointerException_responseObject()
             throws JsonProcessingException {
-        AwsProxyResponse resp = exceptionHandler.handle(new NullPointerException());
+        AwsProxyResponseEvent resp = exceptionHandler.handle(new NullPointerException());
 
         assertNotNull(resp);
         assertEquals(502, resp.getStatusCode());
@@ -149,7 +149,7 @@ void streamHandle_InvalidRequestEventException_500State()
 
         assertNotNull(respStream);
         assertTrue(respStream.size() > 0);
-        AwsProxyResponse resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponse.class);
+        AwsProxyResponseEvent resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponseEvent.class);
         assertNotNull(resp);
         assertEquals(500, resp.getStatusCode());
     }
@@ -162,7 +162,7 @@ void streamHandle_InvalidRequestEventException_responseString()
 
         assertNotNull(respStream);
         assertTrue(respStream.size() > 0);
-        AwsProxyResponse resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponse.class);
+        AwsProxyResponseEvent resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponseEvent.class);
         assertNotNull(resp);
         String body = objectMapper.writeValueAsString(new ErrorModel(AwsProxyExceptionHandler.INTERNAL_SERVER_ERROR));
         assertEquals(body, resp.getBody());
@@ -176,7 +176,7 @@ void streamHandle_InvalidRequestEventException_jsonContentTypeHeader()
 
         assertNotNull(respStream);
         assertTrue(respStream.size() > 0);
-        AwsProxyResponse resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponse.class);
+        AwsProxyResponseEvent resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponseEvent.class);
         assertNotNull(resp);
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
         assertEquals(MediaType.APPLICATION_JSON, resp.getMultiValueHeaders().get(HttpHeaders.CONTENT_TYPE).get(0));
@@ -190,7 +190,7 @@ void streamHandle_InvalidResponseObjectException_502State()
 
         assertNotNull(respStream);
         assertTrue(respStream.size() > 0);
-        AwsProxyResponse resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponse.class);
+        AwsProxyResponseEvent resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponseEvent.class);
         assertNotNull(resp);
         assertEquals(502, resp.getStatusCode());
     }
@@ -203,7 +203,7 @@ void streamHandle_InvalidResponseObjectException_responseString()
 
         assertNotNull(respStream);
         assertTrue(respStream.size() > 0);
-        AwsProxyResponse resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponse.class);
+        AwsProxyResponseEvent resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponseEvent.class);
         assertNotNull(resp);
         String body = objectMapper.writeValueAsString(new ErrorModel(AwsProxyExceptionHandler.GATEWAY_TIMEOUT_ERROR));
         assertEquals(body, resp.getBody());
@@ -217,7 +217,7 @@ void streamHandle_InvalidResponseObjectException_jsonContentTypeHeader()
 
         assertNotNull(respStream);
         assertTrue(respStream.size() > 0);
-        AwsProxyResponse resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponse.class);
+        AwsProxyResponseEvent resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponseEvent.class);
         assertNotNull(resp);
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
         assertEquals(MediaType.APPLICATION_JSON, resp.getMultiValueHeaders().get(HttpHeaders.CONTENT_TYPE).get(0));
diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsProxyExceptionHandlerTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsProxyExceptionHandlerTest.java
index c6083b59b..164fc93ba 100644
--- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsProxyExceptionHandlerTest.java
+++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsProxyExceptionHandlerTest.java
@@ -4,8 +4,8 @@
 import com.amazonaws.serverless.exceptions.InvalidRequestEventException;
 import com.amazonaws.serverless.exceptions.InvalidResponseObjectException;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.model.ErrorModel;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 
@@ -40,7 +40,7 @@ public void setUp() {
 
     @Test
     void typedHandle_InvalidRequestEventException_500State() {
-        AwsProxyResponse resp = exceptionHandler.handle(new InvalidRequestEventException(INVALID_REQUEST_MESSAGE, null));
+        AwsProxyResponseEvent resp = exceptionHandler.handle(new InvalidRequestEventException(INVALID_REQUEST_MESSAGE, null));
 
         assertNotNull(resp);
         assertEquals(500, resp.getStatusCode());
@@ -49,7 +49,7 @@ void typedHandle_InvalidRequestEventException_500State() {
     @Test
     void typedHandle_InvalidRequestEventException_responseString()
             throws JsonProcessingException {
-        AwsProxyResponse resp = exceptionHandler.handle(new InvalidRequestEventException(INVALID_REQUEST_MESSAGE, null));
+        AwsProxyResponseEvent resp = exceptionHandler.handle(new InvalidRequestEventException(INVALID_REQUEST_MESSAGE, null));
 
         assertNotNull(resp);
         String body = objectMapper.writeValueAsString(new ErrorModel(AwsProxyExceptionHandler.INTERNAL_SERVER_ERROR));
@@ -58,7 +58,7 @@ void typedHandle_InvalidRequestEventException_responseString()
 
     @Test
     void typedHandle_InvalidRequestEventException_jsonContentTypeHeader() {
-        AwsProxyResponse resp = exceptionHandler.handle(new InvalidRequestEventException(INVALID_REQUEST_MESSAGE, null));
+        AwsProxyResponseEvent resp = exceptionHandler.handle(new InvalidRequestEventException(INVALID_REQUEST_MESSAGE, null));
 
         assertNotNull(resp);
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
@@ -67,7 +67,7 @@ void typedHandle_InvalidRequestEventException_jsonContentTypeHeader() {
 
     @Test
     void typedHandle_InvalidResponseObjectException_502State() {
-        AwsProxyResponse resp = exceptionHandler.handle(new InvalidResponseObjectException(INVALID_RESPONSE_MESSAGE, null));
+        AwsProxyResponseEvent resp = exceptionHandler.handle(new InvalidResponseObjectException(INVALID_RESPONSE_MESSAGE, null));
 
         assertNotNull(resp);
         assertEquals(502, resp.getStatusCode());
@@ -76,7 +76,7 @@ void typedHandle_InvalidResponseObjectException_502State() {
     @Test
     void typedHandle_InvalidResponseObjectException_responseString()
             throws JsonProcessingException {
-        AwsProxyResponse resp = exceptionHandler.handle(new InvalidResponseObjectException(INVALID_RESPONSE_MESSAGE, null));
+        AwsProxyResponseEvent resp = exceptionHandler.handle(new InvalidResponseObjectException(INVALID_RESPONSE_MESSAGE, null));
 
         assertNotNull(resp);
         String body = objectMapper.writeValueAsString(new ErrorModel(AwsProxyExceptionHandler.GATEWAY_TIMEOUT_ERROR));
@@ -85,7 +85,7 @@ void typedHandle_InvalidResponseObjectException_responseString()
 
     @Test
     void typedHandle_InvalidResponseObjectException_jsonContentTypeHeader() {
-        AwsProxyResponse resp = exceptionHandler.handle(new InvalidResponseObjectException(INVALID_RESPONSE_MESSAGE, null));
+        AwsProxyResponseEvent resp = exceptionHandler.handle(new InvalidResponseObjectException(INVALID_RESPONSE_MESSAGE, null));
 
         assertNotNull(resp);
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
@@ -99,7 +99,7 @@ void typedHandle_InternalServerErrorException_500State() {
         InternalServerErrorException mockInternalServerErrorException = Mockito.mock(InternalServerErrorException.class);
         Mockito.when(mockInternalServerErrorException.getMessage()).thenReturn(INTERNAL_SERVER_ERROR_MESSAGE);
 
-        AwsProxyResponse resp = exceptionHandler.handle(mockInternalServerErrorException);
+        AwsProxyResponseEvent resp = exceptionHandler.handle(mockInternalServerErrorException);
 
         assertNotNull(resp);
         assertEquals(500, resp.getStatusCode());
@@ -111,7 +111,7 @@ void typedHandle_InternalServerErrorException_responseString()
         InternalServerErrorException mockInternalServerErrorException = Mockito.mock(InternalServerErrorException.class);
         Mockito.when(mockInternalServerErrorException.getMessage()).thenReturn(INTERNAL_SERVER_ERROR_MESSAGE);
 
-        AwsProxyResponse resp = exceptionHandler.handle(mockInternalServerErrorException);
+        AwsProxyResponseEvent resp = exceptionHandler.handle(mockInternalServerErrorException);
 
         assertNotNull(resp);
         String body = objectMapper.writeValueAsString(new ErrorModel(AwsProxyExceptionHandler.INTERNAL_SERVER_ERROR));
@@ -123,7 +123,7 @@ void typedHandle_InternalServerErrorException_jsonContentTypeHeader() {
         InternalServerErrorException mockInternalServerErrorException = Mockito.mock(InternalServerErrorException.class);
         Mockito.when(mockInternalServerErrorException.getMessage()).thenReturn(INTERNAL_SERVER_ERROR_MESSAGE);
 
-        AwsProxyResponse resp = exceptionHandler.handle(mockInternalServerErrorException);
+        AwsProxyResponseEvent resp = exceptionHandler.handle(mockInternalServerErrorException);
 
         assertNotNull(resp);
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
@@ -133,7 +133,7 @@ void typedHandle_InternalServerErrorException_jsonContentTypeHeader() {
     @Test
     void typedHandle_NullPointerException_responseObject()
             throws JsonProcessingException {
-        AwsProxyResponse resp = exceptionHandler.handle(new NullPointerException());
+        AwsProxyResponseEvent resp = exceptionHandler.handle(new NullPointerException());
 
         assertNotNull(resp);
         assertEquals(502, resp.getStatusCode());
@@ -151,7 +151,7 @@ void streamHandle_InvalidRequestEventException_500State()
 
         assertNotNull(respStream);
         assertTrue(respStream.size() > 0);
-        AwsProxyResponse resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponse.class);
+        AwsProxyResponseEvent resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponseEvent.class);
         assertNotNull(resp);
         assertEquals(500, resp.getStatusCode());
     }
@@ -164,7 +164,7 @@ void streamHandle_InvalidRequestEventException_responseString()
 
         assertNotNull(respStream);
         assertTrue(respStream.size() > 0);
-        AwsProxyResponse resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponse.class);
+        AwsProxyResponseEvent resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponseEvent.class);
         assertNotNull(resp);
         String body = objectMapper.writeValueAsString(new ErrorModel(AwsProxyExceptionHandler.INTERNAL_SERVER_ERROR));
         assertEquals(body, resp.getBody());
@@ -178,7 +178,7 @@ void streamHandle_InvalidRequestEventException_jsonContentTypeHeader()
 
         assertNotNull(respStream);
         assertTrue(respStream.size() > 0);
-        AwsProxyResponse resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponse.class);
+        AwsProxyResponseEvent resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponseEvent.class);
         assertNotNull(resp);
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
         assertEquals(MediaType.APPLICATION_JSON, AwsHttpServletRequestHelper.getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE));
@@ -192,7 +192,7 @@ void streamHandle_InvalidResponseObjectException_502State()
 
         assertNotNull(respStream);
         assertTrue(respStream.size() > 0);
-        AwsProxyResponse resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponse.class);
+        AwsProxyResponseEvent resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponseEvent.class);
         assertNotNull(resp);
         assertEquals(502, resp.getStatusCode());
     }
@@ -205,7 +205,7 @@ void streamHandle_InvalidResponseObjectException_responseString()
 
         assertNotNull(respStream);
         assertTrue(respStream.size() > 0);
-        AwsProxyResponse resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponse.class);
+        AwsProxyResponseEvent resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponseEvent.class);
         assertNotNull(resp);
         String body = objectMapper.writeValueAsString(new ErrorModel(AwsProxyExceptionHandler.GATEWAY_TIMEOUT_ERROR));
         assertEquals(body, resp.getBody());
@@ -219,7 +219,7 @@ void streamHandle_InvalidResponseObjectException_jsonContentTypeHeader()
 
         assertNotNull(respStream);
         assertTrue(respStream.size() > 0);
-        AwsProxyResponse resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponse.class);
+        AwsProxyResponseEvent resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponseEvent.class);
         assertNotNull(resp);
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
         assertEquals(MediaType.APPLICATION_JSON, AwsHttpServletRequestHelper.getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE));
diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandlerTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandlerTest.java
index 9a1ecf077..acfc98f64 100644
--- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandlerTest.java
+++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandlerTest.java
@@ -7,8 +7,8 @@
 import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletResponseWriter;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import org.apache.hc.client5.http.impl.classic.RequestAbortedException;
 import org.junit.jupiter.api.Test;
@@ -23,7 +23,7 @@ public class LambdaContainerHandlerTest {
     private boolean throwException = false;
 
     ExceptionContainerHandlerTest handler = new ExceptionContainerHandlerTest(
-            APIGatewayProxyRequestEvent.class, AwsProxyResponse.class,
+            APIGatewayProxyRequestEvent.class, AwsProxyResponseEvent.class,
             new AwsProxyHttpServletRequestReader(), new AwsProxyHttpServletResponseWriter(),
             new AwsProxySecurityContextWriter(), new AwsProxyExceptionHandler(), new InitializationWrapper()
     );
@@ -64,17 +64,17 @@ void throwNonRuntime_returnsWrappedException() {
     void noException_returnsResponse() {
         throwException = false;
         LambdaContainerHandler.getContainerConfig().setDisableExceptionMapper(false);
-        AwsProxyResponse resp = handler.proxy(new AwsProxyRequestBuilder("/test", "GET").build(), new MockLambdaContext());
+        AwsProxyResponseEvent resp = handler.proxy(new AwsProxyRequestBuilder("/test", "GET").build(), new MockLambdaContext());
         assertEquals(200, resp.getStatusCode());
         assertEquals("OK", resp.getBody());
     }
 
-    public class ExceptionContainerHandlerTest extends LambdaContainerHandler {
+    public class ExceptionContainerHandlerTest extends LambdaContainerHandler {
 
         public static final String RUNTIME_MESSAGE = "test RuntimeException";
         public static final String NON_RUNTIME_MESSAGE = "test NonRuntimeException";
 
-        protected ExceptionContainerHandlerTest(Class requestClass, Class responseClass, RequestReader requestReader, ResponseWriter responseWriter, SecurityContextWriter securityContextWriter, ExceptionHandler exceptionHandler, InitializationWrapper init) {
+        protected ExceptionContainerHandlerTest(Class requestClass, Class responseClass, RequestReader requestReader, ResponseWriter responseWriter, SecurityContextWriter securityContextWriter, ExceptionHandler exceptionHandler, InitializationWrapper init) {
             super(requestClass, responseClass, requestReader, responseWriter, securityContextWriter, exceptionHandler, init);
         }
 
diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletResponseWriterTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletResponseWriterTest.java
index d5d9ff91c..342ddca8b 100644
--- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletResponseWriterTest.java
+++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletResponseWriterTest.java
@@ -4,9 +4,9 @@
 import com.amazonaws.serverless.exceptions.InvalidResponseObjectException;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.model.ContainerConfig;
 import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import jakarta.servlet.http.HttpServletRequest;
 import org.junit.jupiter.api.Test;
 
@@ -26,17 +26,17 @@ void writeResponse_returnsValidResponse() throws InvalidRequestEventException, I
         AwsHttpServletResponse response = new AwsHttpServletResponse(servletRequest, new CountDownLatch(1));
         response.setAwsResponseBodyString("Random string");
 
-        AwsProxyResponse res3 = responseWriter.writeResponse(response, new MockLambdaContext());
+        AwsProxyResponseEvent res3 = responseWriter.writeResponse(response, new MockLambdaContext());
         assertTrue(res3.getHeaders().isEmpty());
 
         response.setContentType("application/octet-stream");
-        AwsProxyResponse res2 = responseWriter.writeResponse(response, new MockLambdaContext());
-        assertTrue(res2.isBase64Encoded());
+        AwsProxyResponseEvent res2 = responseWriter.writeResponse(response, new MockLambdaContext());
+        assertTrue(res2.getIsBase64Encoded());
 
         response.setHeader("Connection", "Keep-Alive");
         response.setContentType("application/octet-stream;");
         response.setStatus(200);
-        AwsProxyResponse res1 = responseWriter.writeResponse(response, new MockLambdaContext());
+        AwsProxyResponseEvent res1 = responseWriter.writeResponse(response, new MockLambdaContext());
         assertNotNull(res1);
         assertEquals(200, res1.getStatusCode());
     }
diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAsyncContextTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAsyncContextTest.java
index 4c969875f..656abdfa5 100644
--- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAsyncContextTest.java
+++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAsyncContextTest.java
@@ -7,8 +7,8 @@
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import org.junit.jupiter.api.Test;
 
@@ -86,13 +86,13 @@ private AwsServletContext getCtx() {
         return ctx;
     }
 
-    public static class MockContainerHandler extends AwsLambdaServletContainerHandler {
+    public static class MockContainerHandler extends AwsLambdaServletContainerHandler {
         private int desiredStatus;
         private HttpServletResponse response;
         private Servlet selectedServlet;
 
         public MockContainerHandler() {
-            super(APIGatewayProxyRequestEvent.class, AwsProxyResponse.class, new AwsProxyHttpServletRequestReader(), new AwsProxyHttpServletResponseWriter(), new AwsProxySecurityContextWriter(), new AwsProxyExceptionHandler());
+            super(APIGatewayProxyRequestEvent.class, AwsProxyResponseEvent.class, new AwsProxyHttpServletRequestReader(), new AwsProxyHttpServletResponseWriter(), new AwsProxySecurityContextWriter(), new AwsProxyExceptionHandler());
             desiredStatus = 200;
         }
 
diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyRequestDispatcherTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyRequestDispatcherTest.java
index dd83099b1..2a6874ac4 100644
--- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyRequestDispatcherTest.java
+++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyRequestDispatcherTest.java
@@ -6,9 +6,9 @@
 import com.amazonaws.serverless.proxy.AwsProxySecurityContextWriter;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.model.ContainerConfig;
 import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import org.junit.jupiter.api.Test;
 import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper;
@@ -130,7 +130,7 @@ void include_addsToResponse_appendsCorrectly() throws InvalidRequestEventExcepti
         final String secondPart = "second";
         APIGatewayProxyRequestEvent proxyRequest = new AwsProxyRequestBuilder("/hello", "GET").build();
 
-        AwsProxyResponse resp = mockLambdaHandler((AwsProxyHttpServletRequest req, AwsHttpServletResponse res) -> {
+        AwsProxyResponseEvent resp = mockLambdaHandler((AwsProxyHttpServletRequest req, AwsHttpServletResponse res) -> {
             if (req.getAttribute("cnt") == null) {
                 res.getOutputStream().write(firstPart.getBytes());
                 req.setAttribute("cnt", 1);
@@ -151,7 +151,7 @@ void include_appendsNewHeader_cannotAppendNewHeaders() throws InvalidRequestEven
         final String headerKey = "X-Custom-Header";
         APIGatewayProxyRequestEvent proxyRequest = new AwsProxyRequestBuilder("/hello", "GET").build();
 
-        AwsProxyResponse resp = mockLambdaHandler((AwsProxyHttpServletRequest req, AwsHttpServletResponse res) -> {
+        AwsProxyResponseEvent resp = mockLambdaHandler((AwsProxyHttpServletRequest req, AwsHttpServletResponse res) -> {
             if (req.getAttribute("cnt") == null) {
                 res.getOutputStream().write(firstPart.getBytes());
                 req.setAttribute("cnt", 1);
@@ -172,10 +172,10 @@ private interface RequestHandler {
     }
 
 
-    private AwsLambdaServletContainerHandler mockLambdaHandler(RequestHandler h) {
-        return new AwsLambdaServletContainerHandler(
+    private AwsLambdaServletContainerHandler mockLambdaHandler(RequestHandler h) {
+        return new AwsLambdaServletContainerHandler(
                 APIGatewayProxyRequestEvent.class,
-                AwsProxyResponse.class,
+                AwsProxyResponseEvent.class,
                 new AwsProxyHttpServletRequestReader(),
                 new AwsProxyHttpServletResponseWriter(),
                 new AwsProxySecurityContextWriter(),
diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/ServletLambdaContainerHandlerBuilderTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/ServletLambdaContainerHandlerBuilderTest.java
index e6ad77bc0..04d4c7140 100644
--- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/ServletLambdaContainerHandlerBuilderTest.java
+++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/ServletLambdaContainerHandlerBuilderTest.java
@@ -3,8 +3,8 @@
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.AwsProxyExceptionHandler;
 import com.amazonaws.serverless.proxy.AwsProxySecurityContextWriter;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import org.junit.jupiter.api.Test;
 
@@ -41,14 +41,14 @@ void defaultProxy_setsValuesCorrectly() {
         assertTrue(test.responseWriter instanceof AwsProxyHttpServletResponseWriter);
         assertTrue(test.securityContextWriter instanceof AwsProxySecurityContextWriter);
         assertSame(APIGatewayProxyRequestEvent.class, test.requestTypeClass);
-        assertSame(AwsProxyResponse.class, test.responseTypeClass);
+        assertSame(AwsProxyResponseEvent.class, test.responseTypeClass);
         assertEquals("test", test.name);
     }
 
-    public static final class TestHandler extends AwsLambdaServletContainerHandler {
+    public static final class TestHandler extends AwsLambdaServletContainerHandler {
 
         public TestHandler() {
-            super(APIGatewayProxyRequestEvent.class, AwsProxyResponse.class, new AwsProxyHttpServletRequestReader(), new AwsProxyHttpServletResponseWriter(), new AwsProxySecurityContextWriter(), new AwsProxyExceptionHandler());
+            super(APIGatewayProxyRequestEvent.class, AwsProxyResponseEvent.class, new AwsProxyHttpServletRequestReader(), new AwsProxyHttpServletResponseWriter(), new AwsProxySecurityContextWriter(), new AwsProxyExceptionHandler());
         }
         @Override
         protected AwsHttpServletResponse getContainerResponse(HttpServletRequest request, CountDownLatch latch) {
@@ -69,7 +69,7 @@ public void initialize() throws ContainerInitializationException {
     public static final class TestBuilder
             extends ServletLambdaContainerHandlerBuilder<
             APIGatewayProxyRequestEvent,
-            AwsProxyResponse,
+            AwsProxyResponseEvent,
             HttpServletRequest,
             TestHandler,
             TestBuilder> {
diff --git a/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyLambdaContainerHandler.java b/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyLambdaContainerHandler.java
index 8334a6203..3ea99e329 100644
--- a/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyLambdaContainerHandler.java
+++ b/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyLambdaContainerHandler.java
@@ -19,11 +19,11 @@
 import com.amazonaws.serverless.proxy.jersey.suppliers.AwsProxyServletContextSupplier;
 import com.amazonaws.serverless.proxy.jersey.suppliers.AwsProxyServletRequestSupplier;
 import com.amazonaws.serverless.proxy.jersey.suppliers.AwsProxyServletResponseSupplier;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 
 import com.amazonaws.services.lambda.runtime.Context;
 
 import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent;
 import org.glassfish.jersey.internal.inject.AbstractBinder;
@@ -89,10 +89,10 @@ public class JerseyLambdaContainerHandler extends Aws
      *                         ResourceConfig object
      * @return A JerseyLambdaContainerHandler object
      */
-    public static JerseyLambdaContainerHandler getAwsProxyHandler(Application jaxRsApplication) {
-        JerseyLambdaContainerHandler newHandler = new JerseyLambdaContainerHandler<>(
+    public static JerseyLambdaContainerHandler getAwsProxyHandler(Application jaxRsApplication) {
+        JerseyLambdaContainerHandler newHandler = new JerseyLambdaContainerHandler<>(
                 APIGatewayProxyRequestEvent.class,
-                AwsProxyResponse.class,
+                AwsProxyResponseEvent.class,
                 new AwsProxyHttpServletRequestReader(),
                 new AwsProxyHttpServletResponseWriter(),
                 new AwsProxySecurityContextWriter(),
@@ -111,10 +111,10 @@ public static JerseyLambdaContainerHandlerResourceConfig object
      * @return A JerseyLambdaContainerHandler object
      */
-    public static JerseyLambdaContainerHandler getHttpApiV2ProxyHandler(Application jaxRsApplication) {
-        JerseyLambdaContainerHandler newHandler = new JerseyLambdaContainerHandler<>(
+    public static JerseyLambdaContainerHandler getHttpApiV2ProxyHandler(Application jaxRsApplication) {
+        JerseyLambdaContainerHandler newHandler = new JerseyLambdaContainerHandler<>(
                 APIGatewayV2HTTPEvent.class,
-                AwsProxyResponse.class,
+                AwsProxyResponseEvent.class,
                 new AwsHttpApiV2HttpServletRequestReader(),
                 new AwsProxyHttpServletResponseWriter(true),
                 new AwsHttpApiV2SecurityContextWriter(),
@@ -124,10 +124,10 @@ public static JerseyLambdaContainerHandler getAlbProxyHandler(Application jaxRsApplication) {
-        JerseyLambdaContainerHandler newHandler = new JerseyLambdaContainerHandler<>(
+    public static JerseyLambdaContainerHandler getAlbProxyHandler(Application jaxRsApplication) {
+        JerseyLambdaContainerHandler newHandler = new JerseyLambdaContainerHandler<>(
                 ApplicationLoadBalancerRequestEvent.class,
-                AwsProxyResponse.class,
+                AwsProxyResponseEvent.class,
                 new AwsAlbHttpServletRequestReader(),
                 new AwsProxyHttpServletResponseWriter(true),
                 new AwsAlbSecurityContextWriter(),
diff --git a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyTest.java b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyTest.java
index cf21288e2..bdecf23fc 100644
--- a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyTest.java
+++ b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyTest.java
@@ -21,9 +21,9 @@
 import com.amazonaws.serverless.proxy.jersey.model.MapResponseModel;
 import com.amazonaws.serverless.proxy.jersey.model.SingleValueModel;
 import com.amazonaws.serverless.proxy.jersey.providers.ServletRequestFilter;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent;
 import com.fasterxml.jackson.core.JsonProcessingException;
@@ -89,11 +89,11 @@ public class JerseyAwsProxyTest {
             .register(new ResourceBinder())
             .property(LoggingFeature.LOGGING_FEATURE_VERBOSITY_SERVER, LoggingFeature.Verbosity.PAYLOAD_ANY);
 
-    private static JerseyLambdaContainerHandler handler;
-    private static JerseyLambdaContainerHandler httpApiHandler;
-    private static JerseyLambdaContainerHandler albHandler;
+    private static JerseyLambdaContainerHandler handler;
+    private static JerseyLambdaContainerHandler httpApiHandler;
+    private static JerseyLambdaContainerHandler albHandler;
 
-    private static JerseyLambdaContainerHandler handlerWithoutRegisteredDependencies
+    private static JerseyLambdaContainerHandler handlerWithoutRegisteredDependencies
     = JerseyLambdaContainerHandler.getAwsProxyHandler(appWithoutRegisteredDependencies);
 
     private static Context lambdaContext = new MockLambdaContext();
@@ -113,7 +113,7 @@ private AwsProxyRequestBuilder getRequestBuilder(String path, String method) {
         return new AwsProxyRequestBuilder(path, method);
     }
 
-    private AwsProxyResponse executeRequest(AwsProxyRequestBuilder requestBuilder, Context lambdaContext) {
+    private AwsProxyResponseEvent executeRequest(AwsProxyRequestBuilder requestBuilder, Context lambdaContext) {
         switch (type) {
             case "API_GW":
                 if (handler == null) {
@@ -155,7 +155,7 @@ void headers_getHeaders_echo(String reqType) {
         .json()
         .header(CUSTOM_HEADER_KEY, CUSTOM_HEADER_VALUE);
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
@@ -170,7 +170,7 @@ void headers_servletRequest_echo(String reqType) {
         .json()
         .header(CUSTOM_HEADER_KEY, CUSTOM_HEADER_VALUE);
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
@@ -187,7 +187,7 @@ void headers_servletRequest_failedDependencyInjection_expectInternalServerError(
         .header(CUSTOM_HEADER_KEY, CUSTOM_HEADER_VALUE)
         .build();
 
-        AwsProxyResponse output = handlerWithoutRegisteredDependencies.proxy(request, lambdaContext);
+        AwsProxyResponseEvent output = handlerWithoutRegisteredDependencies.proxy(request, lambdaContext);
         assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
         assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), output.getStatusCode());
     }
@@ -199,7 +199,7 @@ void context_servletResponse_setCustomHeader(String reqType) {
         AwsProxyRequestBuilder request = getRequestBuilder("/echo/servlet-response", "GET")
         .json();
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertTrue(output.getMultiValueHeaders().containsKey(EchoJerseyResource.SERVLET_RESP_HEADER_KEY));
     }
@@ -209,7 +209,7 @@ void context_servletResponse_setCustomHeader(String reqType) {
     void context_serverInfo_correctContext(String reqType) {
         initJerseyAwsProxyTest(reqType);
         AwsProxyRequestBuilder request = getRequestBuilder("/echo/servlet-context", "GET");
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
@@ -223,7 +223,7 @@ void requestScheme_valid_expectHttps(String reqType) {
         AwsProxyRequestBuilder request = getRequestBuilder("/echo/scheme", "GET")
         .json();
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
@@ -237,7 +237,7 @@ void requestFilter_injectsServletRequest_expectCustomAttribute(String reqType) {
         AwsProxyRequestBuilder request = getRequestBuilder("/echo/filter-attribute", "GET")
         .json();
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
@@ -253,7 +253,7 @@ void authorizer_securityContext_customPrincipalSuccess(String reqType) {
         .json()
         .authorizerPrincipal(AUTHORIZER_PRINCIPAL_ID);
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
         validateSingleValueModel(output, AUTHORIZER_PRINCIPAL_ID);
@@ -270,7 +270,7 @@ void authorizer_securityContext_customAuthorizerContextSuccess(String reqType) {
         .authorizerContextValue(CUSTOM_HEADER_KEY, CUSTOM_HEADER_VALUE)
         .queryString("key", CUSTOM_HEADER_KEY);
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
@@ -283,7 +283,7 @@ void errors_unknownRoute_expect404(String reqType) {
         initJerseyAwsProxyTest(reqType);
         AwsProxyRequestBuilder request = getRequestBuilder("/echo/test33", "GET");
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(404, output.getStatusCode());
     }
 
@@ -295,7 +295,7 @@ void error_contentType_invalidContentType(String reqType) {
         .header("Content-Type", "application/octet-stream")
         .body("asdasdasd");
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(415, output.getStatusCode());
     }
 
@@ -307,7 +307,7 @@ void error_statusCode_methodNotAllowed(String reqType) {
         .json()
         .queryString("status", "201");
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(405, output.getStatusCode());
     }
 
@@ -321,7 +321,7 @@ void responseBody_responseWriter_validBody(String reqType) throws JsonProcessing
         .json()
         .body(objectMapper.writeValueAsString(singleValueModel));
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertNotNull(output.getBody());
 
@@ -336,7 +336,7 @@ void statusCode_responseStatusCode_customStatusCode(String reqType) {
         .json()
         .queryString("status", "201");
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(201, output.getStatusCode());
     }
 
@@ -346,7 +346,7 @@ void base64_binaryResponse_base64Encoding(String reqType) {
         initJerseyAwsProxyTest(reqType);
         AwsProxyRequestBuilder request = getRequestBuilder("/echo/binary", "GET");
 
-        AwsProxyResponse response = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent response = executeRequest(request, lambdaContext);
         assertNotNull(response.getBody());
         assertTrue(Base64.isBase64(response.getBody()));
     }
@@ -357,7 +357,7 @@ void exception_mapException_mapToNotImplemented(String reqType) {
         initJerseyAwsProxyTest(reqType);
         AwsProxyRequestBuilder request = getRequestBuilder("/echo/exception", "GET");
 
-        AwsProxyResponse response = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent response = executeRequest(request, lambdaContext);
         assertNotNull(response.getBody());
         assertEquals(EchoJerseyResource.EXCEPTION_MESSAGE, response.getBody());
         assertEquals(Response.Status.NOT_IMPLEMENTED.getStatusCode(), response.getStatusCode());
@@ -371,7 +371,7 @@ void stripBasePath_route_shouldRouteCorrectly(String reqType) {
         .json()
         .queryString("status", "201");
         getHandler().stripBasePath("/custompath");
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(201, output.getStatusCode());
         getHandler().stripBasePath("");
     }
@@ -387,7 +387,7 @@ void stripBasePath_route_shouldReturn404WithStageAsContext(String reqType) {
         .queryString("status", "201");
         getHandler().stripBasePath("/custompath");
         LambdaContainerHandler.getContainerConfig().setUseStageAsServletContext(true);
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(404, output.getStatusCode());
         getHandler().stripBasePath("");
         LambdaContainerHandler.getContainerConfig().setUseStageAsServletContext(false);
@@ -401,7 +401,7 @@ void stripBasePath_route_shouldReturn404(String reqType) {
         .json()
         .queryString("status", "201");
         getHandler().stripBasePath("/custom");
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(404, output.getStatusCode());
         getHandler().stripBasePath("");
     }
@@ -414,7 +414,7 @@ void securityContext_injectPrincipal_expectPrincipalName(String reqType) {
         AwsProxyRequestBuilder request = getRequestBuilder("/echo/security-context", "GET")
         .authorizerPrincipal(USER_PRINCIPAL);
 
-        AwsProxyResponse resp = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent resp = executeRequest(request, lambdaContext);
         assertEquals(200, resp.getStatusCode());
         validateSingleValueModel(resp, USER_PRINCIPAL);
     }
@@ -426,7 +426,7 @@ void emptyStream_putNullBody_expectPutToSucceed(String reqType) {
         AwsProxyRequestBuilder request = getRequestBuilder("/echo/empty-stream/" + CUSTOM_HEADER_KEY + "/test/2", "PUT")
         .nullBody()
         .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
-        AwsProxyResponse resp = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent resp = executeRequest(request, lambdaContext);
         assertEquals(200, resp.getStatusCode());
         validateSingleValueModel(resp, CUSTOM_HEADER_KEY);
     }
@@ -441,7 +441,7 @@ void refererHeader_headerParam_expectCorrectInjection(String reqType) {
         .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
         .header("Referer", refererValue);
 
-        AwsProxyResponse resp = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent resp = executeRequest(request, lambdaContext);
         assertEquals(200, resp.getStatusCode());
         validateSingleValueModel(resp, refererValue);
     }
@@ -455,17 +455,17 @@ void textPlainContent_plain_responseHonorsContentType(String reqType) {
         .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
         .header(HttpHeaders.ACCEPT, MediaType.TEXT_PLAIN);
 
-        AwsProxyResponse resp = executeRequest(req, lambdaContext);
+        AwsProxyResponseEvent resp = executeRequest(req, lambdaContext);
         assertEquals(200, resp.getStatusCode());
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
         assertEquals(MediaType.TEXT_PLAIN, resp.getMultiValueHeaders().get(HttpHeaders.CONTENT_TYPE).get(0));
     }
 
-    private void validateMapResponseModel(AwsProxyResponse output) {
+    private void validateMapResponseModel(AwsProxyResponseEvent output) {
         validateMapResponseModel(output, CUSTOM_HEADER_KEY, CUSTOM_HEADER_VALUE);
     }
 
-    private void validateMapResponseModel(AwsProxyResponse output, String key, String value) {
+    private void validateMapResponseModel(AwsProxyResponseEvent output, String key, String value) {
         try {
             MapResponseModel response = objectMapper.readValue(output.getBody(), MapResponseModel.class);
             assertNotNull(response.getValues().get(key));
@@ -476,7 +476,7 @@ private void validateMapResponseModel(AwsProxyResponse output, String key, Strin
         }
     }
 
-    private void validateSingleValueModel(AwsProxyResponse output, String value) {
+    private void validateSingleValueModel(AwsProxyResponseEvent output, String value) {
         try {
             SingleValueModel response = objectMapper.readValue(output.getBody(), SingleValueModel.class);
             assertNotNull(response.getValue());
diff --git a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyInjectionTest.java b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyInjectionTest.java
index 8d9802c51..bf10d766c 100644
--- a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyInjectionTest.java
+++ b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyInjectionTest.java
@@ -15,6 +15,7 @@
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import jakarta.inject.Singleton;
 
@@ -22,7 +23,6 @@
 import org.glassfish.jersey.media.multipart.MultiPartFeature;
 import org.glassfish.jersey.server.ResourceConfig;
 import org.junit.jupiter.api.Test;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 
 /**
  * Test that one can access the Jersey injection manager
@@ -42,7 +42,7 @@ protected void configure() {
     private static ResourceConfig app = new ResourceConfig().register(MultiPartFeature.class)
                                                             .register(new ResourceBinder());
 
-    private static JerseyLambdaContainerHandler handler = JerseyLambdaContainerHandler.getAwsProxyHandler(
+    private static JerseyLambdaContainerHandler handler = JerseyLambdaContainerHandler.getAwsProxyHandler(
             app);
 
     @Test
diff --git a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyParamEncodingTest.java b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyParamEncodingTest.java
index bdf11a4bc..b367813bb 100644
--- a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyParamEncodingTest.java
+++ b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyParamEncodingTest.java
@@ -8,10 +8,10 @@
 import com.amazonaws.serverless.proxy.jersey.model.MapResponseModel;
 import com.amazonaws.serverless.proxy.jersey.model.SingleValueModel;
 import com.amazonaws.serverless.proxy.jersey.providers.ServletRequestFilter;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.services.lambda.runtime.Context;
 
 import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -70,14 +70,14 @@ public class JerseyParamEncodingTest {
     .register(new ResourceBinder())
     .property("jersey.config.server.tracing.type", "ALL")
     .property("jersey.config.server.tracing.threshold", "VERBOSE");
-    private static JerseyLambdaContainerHandler handler;
+    private static JerseyLambdaContainerHandler handler;
 
     private static ResourceConfig httpApiApp = new ResourceConfig().packages("com.amazonaws.serverless.proxy.jersey")
     .register(MultiPartFeature.class)
     .register(new ResourceBinder())
     .property("jersey.config.server.tracing.type", "ALL")
     .property("jersey.config.server.tracing.threshold", "VERBOSE");
-    private static JerseyLambdaContainerHandler httpApiHandler;
+    private static JerseyLambdaContainerHandler httpApiHandler;
 
     private static ResourceConfig albApp = new ResourceConfig().packages("com.amazonaws.serverless.proxy.jersey")
             .register(LoggingFeature.class)
@@ -85,7 +85,7 @@ public class JerseyParamEncodingTest {
             .register(MultiPartFeature.class)
             .register(new ResourceBinder())
             .property(LoggingFeature.LOGGING_FEATURE_VERBOSITY_SERVER, LoggingFeature.Verbosity.PAYLOAD_ANY);
-    private static JerseyLambdaContainerHandler albHandler;
+    private static JerseyLambdaContainerHandler albHandler;
 
     private static Context lambdaContext = new MockLambdaContext();
 
@@ -104,7 +104,7 @@ private AwsProxyRequestBuilder getRequestBuilder(String path, String method) {
         return new AwsProxyRequestBuilder(path, method);
     }
 
-    private AwsProxyResponse executeRequest(AwsProxyRequestBuilder requestBuilder, Context lambdaContext) {
+    private AwsProxyResponseEvent executeRequest(AwsProxyRequestBuilder requestBuilder, Context lambdaContext) {
         switch (type) {
             case "API_GW":
                 if (handler == null) {
@@ -134,7 +134,7 @@ void queryString_uriInfo_echo(String reqType) {
         .json()
         .queryString(QUERY_STRING_KEY, QUERY_STRING_NON_ENCODED_VALUE);
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
@@ -149,7 +149,7 @@ void queryString_notEncoded_echo(String reqType) {
         .json()
         .queryString(QUERY_STRING_KEY, QUERY_STRING_NON_ENCODED_VALUE);
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
@@ -165,7 +165,7 @@ void queryString_encoded_echo(String reqType) {
         .json()
         .queryString(QUERY_STRING_KEY, QUERY_STRING_ENCODED_VALUE);
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
@@ -178,7 +178,7 @@ void simpleQueryParam_encoding_expectDecodedParam(String reqType) {
         initJerseyParamEncodingTest(reqType);
         AwsProxyRequestBuilder request = getRequestBuilder("/echo/decoded-param", "GET").queryString("param", SIMPLE_ENCODED_PARAM);
 
-        AwsProxyResponse resp = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent resp = executeRequest(request, lambdaContext);
         assertEquals(200, resp.getStatusCode());
         validateSingleValueModel(resp, SIMPLE_ENCODED_PARAM);
     }
@@ -189,7 +189,7 @@ void jsonQueryParam_encoding_expectDecodedParam(String reqType) {
         initJerseyParamEncodingTest(reqType);
         AwsProxyRequestBuilder request = getRequestBuilder("/echo/decoded-param", "GET").queryString("param", JSON_ENCODED_PARAM);
 
-        AwsProxyResponse resp = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent resp = executeRequest(request, lambdaContext);
         assertEquals(200, resp.getStatusCode());
         validateSingleValueModel(resp, JSON_ENCODED_PARAM);
     }
@@ -205,7 +205,7 @@ void simpleQueryParam_encoding_expectEncodedParam(String reqType) {
         } catch (UnsupportedEncodingException e) {
             fail("Could not encode parameter value");
         }
-        AwsProxyResponse resp = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent resp = executeRequest(request, lambdaContext);
         assertEquals(200, resp.getStatusCode());
         validateSingleValueModel(resp, encodedVal);
     }
@@ -221,7 +221,7 @@ void jsonQueryParam_encoding_expectEncodedParam(String reqType) {
         } catch (UnsupportedEncodingException e) {
             fail("Could not encode parameter value");
         }
-        AwsProxyResponse resp = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent resp = executeRequest(request, lambdaContext);
         assertEquals(200, resp.getStatusCode());
         validateSingleValueModel(resp, encodedVal);
     }
@@ -232,7 +232,7 @@ void queryParam_encoding_expectFullyEncodedUrl(String reqType) {
         initJerseyParamEncodingTest(reqType);
         String paramValue = "/+=";
         AwsProxyRequestBuilder request = getRequestBuilder("/echo/encoded-param", "GET").queryString("param", paramValue);
-        AwsProxyResponse resp = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent resp = executeRequest(request, lambdaContext);
         assertNotNull(resp);
         assertEquals(200, resp.getStatusCode());
         validateSingleValueModel(resp, "%2F%2B%3D");
@@ -245,7 +245,7 @@ void pathParam_encoded_routesToCorrectPath(String reqType) {
         String encodedParam = "http%3A%2F%2Fhelloresource.com";
         String path = "/echo/encoded-path/" + encodedParam;
         AwsProxyRequestBuilder request = getRequestBuilder(path, "GET");
-        AwsProxyResponse resp = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent resp = executeRequest(request, lambdaContext);
         assertNotNull(resp);
         assertEquals(200, resp.getStatusCode());
         validateSingleValueModel(resp, encodedParam);
@@ -258,7 +258,7 @@ void pathParam_encoded_returns404(String reqType) {
         String encodedParam = "http://helloresource.com";
         String path = "/echo/encoded-path/" + encodedParam;
         AwsProxyRequestBuilder request = getRequestBuilder(path, "GET");
-        AwsProxyResponse resp = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent resp = executeRequest(request, lambdaContext);
         assertNotNull(resp);
         assertEquals(404, resp.getStatusCode());
     }
@@ -269,7 +269,7 @@ void pathParam_encoded_returns404(String reqType) {
     void queryParam_listOfString_expectCorrectLength(String reqType) {
         initJerseyParamEncodingTest(reqType);
         AwsProxyRequestBuilder request = getRequestBuilder("/echo/list-query-string", "GET").queryString("list", "v1,v2,v3");
-        AwsProxyResponse resp = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent resp = executeRequest(request, lambdaContext);
         assertNotNull(resp);
         assertEquals(200, resp.getStatusCode());
         validateSingleValueModel(resp, "3");
@@ -282,13 +282,13 @@ void multipart_getFileSize_expectCorrectLength(String reqType)
         initJerseyParamEncodingTest(reqType);
         AwsProxyRequestBuilder request = getRequestBuilder("/echo/file-size", "POST")
         .formFilePart("file", "myfile.jpg", FILE_CONTENTS);
-        AwsProxyResponse resp = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent resp = executeRequest(request, lambdaContext);
         assertNotNull(resp);
         assertEquals(200, resp.getStatusCode());
         validateSingleValueModel(resp, "" + FILE_CONTENTS.length);
     }
 
-    private void validateSingleValueModel(AwsProxyResponse output, String value) {
+    private void validateSingleValueModel(AwsProxyResponseEvent output, String value) {
         try {
             SingleValueModel response = objectMapper.readValue(output.getBody(), SingleValueModel.class);
             assertNotNull(response.getValue());
@@ -299,7 +299,7 @@ private void validateSingleValueModel(AwsProxyResponse output, String value) {
         }
     }
 
-    private void validateMapResponseModel(AwsProxyResponse output, String key, String value) {
+    private void validateMapResponseModel(AwsProxyResponseEvent output, String key, String value) {
         try {
             MapResponseModel response = objectMapper.readValue(output.getBody(), MapResponseModel.class);
             assertNotNull(response.getValues().get(key));
diff --git a/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringLambdaContainerHandler.java b/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringLambdaContainerHandler.java
index 00d9d49fa..4f6787075 100644
--- a/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringLambdaContainerHandler.java
+++ b/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringLambdaContainerHandler.java
@@ -15,10 +15,10 @@
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.*;
 import com.amazonaws.serverless.proxy.internal.testutils.Timer;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.internal.servlet.*;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent;
 import org.springframework.web.context.ConfigurableWebApplicationContext;
@@ -50,7 +50,7 @@ public class SpringLambdaContainerHandler extends Aws
      * @return An initialized instance of the `SpringLambdaContainerHandler`
      * @throws ContainerInitializationException When the Spring framework fails to start.
      */
-    public static SpringLambdaContainerHandler getAwsProxyHandler(Class... config) throws ContainerInitializationException {
+    public static SpringLambdaContainerHandler getAwsProxyHandler(Class... config) throws ContainerInitializationException {
         return new SpringProxyHandlerBuilder()
                 .defaultProxy()
                 .initializationWrapper(new InitializationWrapper())
@@ -65,7 +65,7 @@ public static SpringLambdaContainerHandler getAwsProxyHandler(ConfigurableWebApplicationContext applicationContext, String... profiles)
+    public static SpringLambdaContainerHandler getAwsProxyHandler(ConfigurableWebApplicationContext applicationContext, String... profiles)
             throws ContainerInitializationException {
         return new SpringProxyHandlerBuilder()
                 .defaultProxy()
@@ -81,7 +81,7 @@ public static SpringLambdaContainerHandler getHttpApiV2ProxyHandler(Class... config) throws ContainerInitializationException {
+    public static SpringLambdaContainerHandler getHttpApiV2ProxyHandler(Class... config) throws ContainerInitializationException {
         return new SpringProxyHandlerBuilder()
                 .defaultHttpApiV2Proxy()
                 .initializationWrapper(new InitializationWrapper())
@@ -89,7 +89,7 @@ public static SpringLambdaContainerHandler getAlbProxyHandler(Class... config) throws ContainerInitializationException {
+    public static SpringLambdaContainerHandler getAlbProxyHandler(Class... config) throws ContainerInitializationException {
         return new SpringProxyHandlerBuilder()
                 .defaultAlbProxy()
                 .initializationWrapper(new InitializationWrapper())
diff --git a/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringProxyHandlerBuilder.java b/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringProxyHandlerBuilder.java
index 5614b94df..5a6f74214 100644
--- a/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringProxyHandlerBuilder.java
+++ b/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringProxyHandlerBuilder.java
@@ -14,7 +14,7 @@
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.internal.servlet.ServletLambdaContainerHandlerBuilder;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import org.springframework.web.context.ConfigurableWebApplicationContext;
 import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
 
@@ -22,9 +22,9 @@
 
 public class SpringProxyHandlerBuilder extends ServletLambdaContainerHandlerBuilder<
             RequestType,
-            AwsProxyResponse,
+            AwsProxyResponseEvent,
             HttpServletRequest,
-            SpringLambdaContainerHandler,
+            SpringLambdaContainerHandler,
             SpringProxyHandlerBuilder> {
     private ConfigurableWebApplicationContext springContext;
     private Class[] configurationClasses;
@@ -52,7 +52,7 @@ public SpringProxyHandlerBuilder profiles(String... profiles) {
     }
 
     @Override
-    public SpringLambdaContainerHandler build() throws ContainerInitializationException {
+    public SpringLambdaContainerHandler build() throws ContainerInitializationException {
         validate();
         if (springContext == null && (configurationClasses == null || configurationClasses.length == 0)) {
             throw new ContainerInitializationException("Missing both configuration classes and application context, at least" +
@@ -66,14 +66,14 @@ public SpringLambdaContainerHandler build() throw
             }
         }
 
-        SpringLambdaContainerHandler handler = createHandler(ctx);
+        SpringLambdaContainerHandler handler = createHandler(ctx);
         if (profiles != null) {
             handler.activateSpringProfiles(profiles);
         }
         return handler;
     }
 
-    protected SpringLambdaContainerHandler createHandler(ConfigurableWebApplicationContext ctx) {
+    protected SpringLambdaContainerHandler createHandler(ConfigurableWebApplicationContext ctx) {
         return new SpringLambdaContainerHandler<>(
                 requestTypeClass, responseTypeClass, requestReader, responseWriter,
                 securityContextWriter, exceptionHandler, ctx, initializationWrapper
@@ -81,8 +81,8 @@ protected SpringLambdaContainerHandler createHand
     }
 
     @Override
-    public SpringLambdaContainerHandler buildAndInitialize() throws ContainerInitializationException {
-        SpringLambdaContainerHandler handler = build();
+    public SpringLambdaContainerHandler buildAndInitialize() throws ContainerInitializationException {
+        SpringLambdaContainerHandler handler = build();
         initializationWrapper.start(handler);
         return handler;
     }
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SlowAppTest.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SlowAppTest.java
index 17cf703cd..d57adce97 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SlowAppTest.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SlowAppTest.java
@@ -3,10 +3,10 @@
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.springslowapp.LambdaHandler;
 import com.amazonaws.serverless.proxy.spring.springslowapp.MessageController;
 import com.amazonaws.serverless.proxy.spring.springslowapp.SlowAppConfig;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import org.junit.jupiter.api.Test;
 
@@ -29,7 +29,7 @@ void springSlowApp_continuesInBackgroundThread_returnsCorrect() {
         assertTrue(slowApp.getConstructorTime() < 10_000);
         APIGatewayProxyRequestEvent req = new AwsProxyRequestBuilder("/hello", "GET").build();
         long startRequestTime = Instant.now().toEpochMilli();
-        AwsProxyResponse resp = slowApp.handleRequest(req, new MockLambdaContext());
+        AwsProxyResponseEvent resp = slowApp.handleRequest(req, new MockLambdaContext());
         long endRequestTime = Instant.now().toEpochMilli();
         assertTrue(endRequestTime - startRequestTime > SlowAppConfig.SlowDownInit.INIT_SLEEP_TIME_MS - 10_000);
         assertEquals(200, resp.getStatusCode());
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringAwsProxyTest.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringAwsProxyTest.java
index 0a20ae012..db02dd52c 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringAwsProxyTest.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringAwsProxyTest.java
@@ -17,6 +17,7 @@
 import com.amazonaws.serverless.proxy.spring.echoapp.model.SingleValueModel;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent;
 import com.fasterxml.jackson.core.JsonProcessingException;
@@ -52,9 +53,9 @@ public class SpringAwsProxyTest {
 
     private ObjectMapper objectMapper = new ObjectMapper();
     private MockLambdaContext lambdaContext = new MockLambdaContext();
-    private static SpringLambdaContainerHandler handler;
-    private static SpringLambdaContainerHandler httpApiHandler;
-    private static SpringLambdaContainerHandler albHandler;
+    private static SpringLambdaContainerHandler handler;
+    private static SpringLambdaContainerHandler httpApiHandler;
+    private static SpringLambdaContainerHandler albHandler;
 
     private AwsLambdaServletContainerHandler.StartupHandler h = (c -> {
         FilterRegistration.Dynamic registration = c.addFilter("UnauthenticatedFilter", UnauthenticatedFilter.class);
@@ -76,7 +77,7 @@ public void initSpringAwsProxyTest(String reqType) {
         type = reqType;
     }
 
-    private AwsProxyResponse executeRequest(AwsProxyRequestBuilder requestBuilder, Context lambdaContext) {
+    private AwsProxyResponseEvent executeRequest(AwsProxyRequestBuilder requestBuilder, Context lambdaContext) {
         try {
             switch (type) {
                 case "API_GW":
@@ -107,7 +108,7 @@ private AwsProxyResponse executeRequest(AwsProxyRequestBuilder requestBuilder, C
         }
     }
 
-    private AwsProxyResponse executeV2Request(AwsProxyRequestBuilder requestBuilder, Context lambdaContext) {
+    private AwsProxyResponseEvent executeV2Request(AwsProxyRequestBuilder requestBuilder, Context lambdaContext) {
         try {
             if (httpApiHandler == null) {
                 httpApiHandler = SpringLambdaContainerHandler.getHttpApiV2ProxyHandler(EchoSpringAppConfig.class);
@@ -135,7 +136,7 @@ void controllerAdvice_invalidPath_returnAdvice(String reqType) {
                 .json()
                 .header(CUSTOM_HEADER_KEY, CUSTOM_HEADER_VALUE);
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertNotNull(output);
         assertEquals(404, output.getStatusCode());
         validateSingleValueModel(output, RestControllerAdvice.ERROR_MESSAGE);
@@ -150,7 +151,7 @@ void headers_getHeaders_echo(String reqType) {
                 .json()
                 .header(CUSTOM_HEADER_KEY, CUSTOM_HEADER_VALUE);
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
         validateMapResponseModel(output);
@@ -164,7 +165,7 @@ void headers_servletRequest_echo(String reqType) {
                 .json()
                 .header(CUSTOM_HEADER_KEY, CUSTOM_HEADER_VALUE);
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
 
@@ -179,7 +180,7 @@ void queryString_uriInfo_echo(String reqType) {
                 .json()
                 .queryString(CUSTOM_HEADER_KEY, CUSTOM_HEADER_VALUE);
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
 
@@ -194,7 +195,7 @@ void queryString_listParameter_expectCorrectLength(String reqType) {
                 .json()
                 .queryString("list", "v1,v2,v3");
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
 
         validateSingleValueModel(output, "3");
@@ -210,7 +211,7 @@ void queryString_multiParam_expectCorrectValueCount(String reqType)
                 .queryString("multiple", "first")
                 .queryString("multiple", "second");
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         MapResponseModel response = objectMapper.readValue(output.getBody(), MapResponseModel.class);
 
@@ -230,7 +231,7 @@ void dateHeader_notModified_expect304(String reqType) {
                         DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now().minus(1, ChronoUnit.SECONDS))
                 );
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(304, output.getStatusCode());
         assertEquals("", output.getBody());
     }
@@ -246,7 +247,7 @@ void dateHeader_notModified_expect200(String reqType) {
                         DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now().minus(5, ChronoUnit.DAYS))
                 );
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertEquals(EchoResource.STRING_BODY, output.getBody());
     }
@@ -260,7 +261,7 @@ void authorizer_securityContext_customPrincipalSuccess(String reqType) {
                 .json()
                 .authorizerPrincipal(AUTHORIZER_PRINCIPAL_ID);
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
 
@@ -273,7 +274,7 @@ void errors_unknownRoute_expect404(String reqType) {
         initSpringAwsProxyTest(reqType);
         AwsProxyRequestBuilder request = new AwsProxyRequestBuilder("/echo/test33", "GET");
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(404, output.getStatusCode());
     }
 
@@ -285,7 +286,7 @@ void error_contentType_invalidContentType(String reqType) {
                 .header("Content-Type", "application/octet-stream")
                 .body("asdasdasd");
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(415, output.getStatusCode());
     }
 
@@ -297,7 +298,7 @@ void error_statusCode_methodNotAllowed(String reqType) {
                 .json()
                 .queryString("status", "201");
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(405, output.getStatusCode());
     }
 
@@ -310,7 +311,7 @@ void error_unauthenticatedCall_filterStepsRequest(String reqType) {
                 .json()
                 .queryString("status", "201");
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(401, output.getStatusCode());
     }
 
@@ -325,7 +326,7 @@ void responseBody_responseWriter_validBody(String reqType) throws JsonProcessing
                 .header("Content-Type", "application/json")
                 .body(objectMapper.writeValueAsString(singleValueModel));
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertNotNull(output.getBody());
         validateSingleValueModel(output, CUSTOM_HEADER_VALUE);
@@ -341,7 +342,7 @@ void responseBody_responseWriter_validBody_UTF(String reqType) throws JsonProces
                 .header("Content-Type", "application/json; charset=UTF-8")
                 .body(objectMapper.writeValueAsString(singleValueModel));
         LambdaContainerHandler.getContainerConfig().setDefaultContentCharset("UTF-8");
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertNotNull(output.getBody());
         validateSingleValueModel(output, UNICODE_VALUE);
@@ -356,7 +357,7 @@ void statusCode_responseStatusCode_customStatusCode(String reqType) {
                 .json()
                 .queryString("status", "201");
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(201, output.getStatusCode());
     }
 
@@ -366,7 +367,7 @@ void base64_binaryResponse_base64Encoding(String reqType) {
         initSpringAwsProxyTest(reqType);
         AwsProxyRequestBuilder request = new AwsProxyRequestBuilder("/echo/binary", "GET");
 
-        AwsProxyResponse response = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent response = executeRequest(request, lambdaContext);
         assertNotNull(response.getBody());
         assertTrue(Base64.isBase64(response.getBody()));
     }
@@ -379,7 +380,7 @@ void injectBody_populatedResponse_noException(String reqType) {
                 .header(HttpHeaders.CONTENT_TYPE, "text/plain")
                 .body("This is a populated body");
 
-        AwsProxyResponse response = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent response = executeRequest(request, lambdaContext);
         assertNotNull(response.getBody());
         assertEquals(200, response.getStatusCode());
         try {
@@ -391,7 +392,7 @@ void injectBody_populatedResponse_noException(String reqType) {
         }
 
         AwsProxyRequestBuilder emptyReq = new AwsProxyRequestBuilder("/echo/request-body", "POST");
-        AwsProxyResponse emptyResp = executeRequest(emptyReq, lambdaContext);
+        AwsProxyResponseEvent emptyResp = executeRequest(emptyReq, lambdaContext);
         try {
             SingleValueModel output = objectMapper.readValue(emptyResp.getBody(), SingleValueModel.class);
             assertNull(output.getValue());
@@ -418,7 +419,7 @@ void servletRequestEncoding_acceptEncoding_okStatusCode(String reqType) {
             fail("Could not serialize object to JSON");
         }
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
     }
 
@@ -428,7 +429,7 @@ void request_requestURI(String reqType) {
         initSpringAwsProxyTest(reqType);
         AwsProxyRequestBuilder request = new AwsProxyRequestBuilder("/echo/request-URI", "GET");
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
 
         validateSingleValueModel(output, "/echo/request-URI");
@@ -443,7 +444,7 @@ void request_requestURL(String reqType) {
                 .serverName("api.myserver.com")
                 .stage("prod");
         handler.stripBasePath("");
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
 
         validateSingleValueModel(output, "https://api.myserver.com/echo/request-url");
@@ -458,7 +459,7 @@ void request_encodedPath_returnsDecodedPath(String reqType) {
                 .serverName("api.myserver.com")
                 .stage("prod");
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
 
         validateSingleValueModel(output, "Some Thing");
@@ -477,7 +478,7 @@ void contextPath_generateLink_returnsCorrectPath(String reqType) {
         LambdaContainerHandler.getContainerConfig().addCustomDomain("api.myserver.com");
         SpringLambdaContainerHandler.getContainerConfig().setUseStageAsServletContext(true);
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
 
         String expectedUri = "https://api.myserver.com/prod/echo/encoded-request-uri/" + EchoResource.TEST_GENERATE_URI;
@@ -495,13 +496,13 @@ void multipart_getFileName_returnsCorrectFileName(String reqType)
         AwsProxyRequestBuilder request = new AwsProxyRequestBuilder("/echo/attachment", "POST")
                 .formFilePart("testFile", "myFile.txt", "hello".getBytes());
 
-        AwsProxyResponse output = executeRequest(request, lambdaContext);
+        AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
 
         assertEquals("testFile", output.getBody());
     }
 
-    private void validateMapResponseModel(AwsProxyResponse output) {
+    private void validateMapResponseModel(AwsProxyResponseEvent output) {
         try {
             MapResponseModel response = objectMapper.readValue(output.getBody(), MapResponseModel.class);
             assertNotNull(response.getValues().get(CUSTOM_HEADER_KEY));
@@ -512,7 +513,7 @@ private void validateMapResponseModel(AwsProxyResponse output) {
         }
     }
 
-    private void validateSingleValueModel(AwsProxyResponse output, String value) {
+    private void validateSingleValueModel(AwsProxyResponseEvent output, String value) {
         try {
             SingleValueModel response = objectMapper.readValue(output.getBody(), SingleValueModel.class);
             assertNotNull(response.getValue());
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringServletContextTest.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringServletContextTest.java
index 899f37cb5..8e2926cb6 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringServletContextTest.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringServletContextTest.java
@@ -3,7 +3,6 @@
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsServletContext;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
@@ -11,6 +10,7 @@
 import com.amazonaws.serverless.proxy.spring.echoapp.CustomHeaderFilter;
 import com.amazonaws.serverless.proxy.spring.echoapp.EchoSpringAppConfig;
 import com.amazonaws.serverless.proxy.spring.echoapp.model.ValidatedUserModel;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
@@ -30,7 +30,7 @@ public class SpringServletContextTest {
     private static final String STAGE = LambdaContainerHandler.SERVER_INFO + "/" + AwsServletContext.SERVLET_API_MAJOR_VERSION + "." + AwsServletContext.SERVLET_API_MINOR_VERSION;
     private MockLambdaContext lambdaContext = new MockLambdaContext();
 
-    private static SpringLambdaContainerHandler handler;
+    private static SpringLambdaContainerHandler handler;
 
     @BeforeAll
     public static void setUp() {
@@ -56,7 +56,7 @@ void context_autowireValidContext_echoContext() {
                 .stage(STAGE)
                 .build();
 
-        AwsProxyResponse output = handler.proxy(request, lambdaContext);
+        AwsProxyResponseEvent output = handler.proxy(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertEquals("text/plain", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
         assertEquals(STAGE, output.getBody());
@@ -69,7 +69,7 @@ void context_contextAware_contextEcho() {
                 .stage(STAGE)
                 .build();
 
-        AwsProxyResponse output = handler.proxy(request, lambdaContext);
+        AwsProxyResponseEvent output = handler.proxy(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
         assertEquals("text/plain", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
         assertEquals(STAGE, output.getBody());
@@ -82,7 +82,7 @@ void filter_customHeaderFilter_echoHeaders() {
                 .stage(STAGE)
                 .build();
 
-        AwsProxyResponse output = handler.proxy(request, lambdaContext);
+        AwsProxyResponseEvent output = handler.proxy(request, lambdaContext);
         assertNotNull(output.getMultiValueHeaders());
         assertTrue(output.getMultiValueHeaders().size() > 0);
         assertNotNull(output.getMultiValueHeaders().get(CustomHeaderFilter.HEADER_NAME));
@@ -98,7 +98,7 @@ void filter_validationFilter_emptyName() {
                 .body(userModel)
                 .build();
 
-        AwsProxyResponse output = handler.proxy(request, lambdaContext);
+        AwsProxyResponseEvent output = handler.proxy(request, lambdaContext);
         assertEquals(HttpStatus.BAD_REQUEST.value(), output.getStatusCode());
     }
 
@@ -109,7 +109,7 @@ void exception_populatedException_annotationValuesMappedCorrectly() {
                 .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
                 .build();
 
-        AwsProxyResponse output = handler.proxy(request, lambdaContext);
+        AwsProxyResponseEvent output = handler.proxy(request, lambdaContext);
 
         assertEquals(409, output.getStatusCode());
         assertTrue(output.getBody().contains(ContextResource.EXCEPTION_REASON));
@@ -122,7 +122,7 @@ void cookie_injectInResponse_expectCustomSetCookie() {
                 .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
                 .build();
 
-        AwsProxyResponse output = handler.proxy(request, lambdaContext);
+        AwsProxyResponseEvent output = handler.proxy(request, lambdaContext);
 
 
         assertEquals(200, output.getStatusCode());
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/StaticAppProxyTest.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/StaticAppProxyTest.java
index 62e1fc3f5..7af021684 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/StaticAppProxyTest.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/StaticAppProxyTest.java
@@ -3,11 +3,11 @@
 
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
 import com.amazonaws.serverless.proxy.spring.staticapp.LambdaHandler;
 
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import org.junit.jupiter.api.Test;
 import org.springframework.http.HttpHeaders;
@@ -27,7 +27,7 @@ void staticPage() {
         APIGatewayProxyRequestEvent req = new AwsProxyRequestBuilder("/sample/page", "GET").build();
         // we temporarily allow the container to read from any path
         LambdaContainerHandler.getContainerConfig().addValidFilePath("/");
-        AwsProxyResponse resp = lambdaHandler.handleRequest(req, new MockLambdaContext());
+        AwsProxyResponseEvent resp = lambdaHandler.handleRequest(req, new MockLambdaContext());
 
         assertEquals(200, resp.getStatusCode());
         assertTrue(resp.getBody().startsWith(""));
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/extensibility/CustomSpringLambdaContainerHandler.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/extensibility/CustomSpringLambdaContainerHandler.java
index 21f2adb17..e483770d3 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/extensibility/CustomSpringLambdaContainerHandler.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/extensibility/CustomSpringLambdaContainerHandler.java
@@ -3,8 +3,8 @@
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.*;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletResponse;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent;
 import org.springframework.web.context.ConfigurableWebApplicationContext;
@@ -20,7 +20,7 @@ public class CustomSpringLambdaContainerHandler exten
      * @return An initialized instance of the `SpringLambdaContainerHandler`
      * @throws ContainerInitializationException When the Spring framework fails to start.
      */
-    public static SpringLambdaContainerHandler getAwsProxyHandler(Class... config) throws ContainerInitializationException {
+    public static SpringLambdaContainerHandler getAwsProxyHandler(Class... config) throws ContainerInitializationException {
         return new CustomSpringProxyHandlerBuilder()
                 .defaultProxy()
                 .initializationWrapper(new InitializationWrapper())
@@ -35,7 +35,7 @@ public static SpringLambdaContainerHandler getAwsProxyHandler(ConfigurableWebApplicationContext applicationContext, String... profiles)
+    public static SpringLambdaContainerHandler getAwsProxyHandler(ConfigurableWebApplicationContext applicationContext, String... profiles)
             throws ContainerInitializationException {
         return new CustomSpringProxyHandlerBuilder()
                 .defaultProxy()
@@ -51,7 +51,7 @@ public static SpringLambdaContainerHandler getHttpApiV2ProxyHandler(Class... config) throws ContainerInitializationException {
+    public static SpringLambdaContainerHandler getHttpApiV2ProxyHandler(Class... config) throws ContainerInitializationException {
         return new CustomSpringProxyHandlerBuilder()
                 .defaultHttpApiV2Proxy()
                 .initializationWrapper(new InitializationWrapper())
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/extensibility/CustomSpringProxyHandlerBuilder.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/extensibility/CustomSpringProxyHandlerBuilder.java
index de74cf547..c2ee9e779 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/extensibility/CustomSpringProxyHandlerBuilder.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/extensibility/CustomSpringProxyHandlerBuilder.java
@@ -1,14 +1,14 @@
 package com.amazonaws.serverless.proxy.spring.extensibility;
 
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler;
 import com.amazonaws.serverless.proxy.spring.SpringProxyHandlerBuilder;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import org.springframework.web.context.ConfigurableWebApplicationContext;
 
 public class CustomSpringProxyHandlerBuilder extends SpringProxyHandlerBuilder {
 
     @Override
-    protected SpringLambdaContainerHandler createHandler(ConfigurableWebApplicationContext ctx) {
+    protected SpringLambdaContainerHandler createHandler(ConfigurableWebApplicationContext ctx) {
         return new CustomSpringLambdaContainerHandler<>(requestTypeClass, responseTypeClass, requestReader, responseWriter,
                 securityContextWriter, exceptionHandler, ctx, initializationWrapper);
     }
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/extensibility/StreamLambdaHandler.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/extensibility/StreamLambdaHandler.java
index 1e3c2066b..fb74384b5 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/extensibility/StreamLambdaHandler.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/extensibility/StreamLambdaHandler.java
@@ -2,10 +2,10 @@
 
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import org.springframework.web.context.support.GenericWebApplicationContext;
 
@@ -15,7 +15,7 @@
 
 
 public class StreamLambdaHandler implements RequestStreamHandler {
-    private static final SpringLambdaContainerHandler handler;
+    private static final SpringLambdaContainerHandler handler;
     static {
         try {
             handler = CustomSpringLambdaContainerHandler.getAwsProxyHandler(new GenericWebApplicationContext());
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/profile/SpringProfileTest.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/profile/SpringProfileTest.java
index cfde9ebee..3946e284c 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/profile/SpringProfileTest.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/profile/SpringProfileTest.java
@@ -1,12 +1,12 @@
 package com.amazonaws.serverless.proxy.spring.profile;
 
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsServletContext;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
 import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler;
 import com.amazonaws.serverless.proxy.spring.echoapp.EchoSpringAppConfig;
 import com.amazonaws.serverless.proxy.spring.echoapp.model.MapResponseModel;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.junit.jupiter.api.BeforeEach;
@@ -42,8 +42,8 @@ void profile_defaultProfile() throws Exception {
         APIGatewayProxyRequestEvent request = new AwsProxyRequestBuilder("/profile/spring-properties", "GET")
                 .build();
 
-        SpringLambdaContainerHandler handler = SpringLambdaContainerHandler.getAwsProxyHandler(EchoSpringAppConfig.class);
-        AwsProxyResponse output = handler.proxy(request, lambdaContext);
+        SpringLambdaContainerHandler handler = SpringLambdaContainerHandler.getAwsProxyHandler(EchoSpringAppConfig.class);
+        AwsProxyResponseEvent output = handler.proxy(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
 
         MapResponseModel response = objectMapper.readValue(output.getBody(), MapResponseModel.class);
@@ -57,9 +57,9 @@ void profile_defaultProfile() throws Exception {
     void profile_overrideProfile() throws Exception {
         APIGatewayProxyRequestEvent request = new AwsProxyRequestBuilder("/profile/spring-properties", "GET")
                 .build();
-        SpringLambdaContainerHandler handler = SpringLambdaContainerHandler.getAwsProxyHandler(EchoSpringAppConfig.class);
+        SpringLambdaContainerHandler handler = SpringLambdaContainerHandler.getAwsProxyHandler(EchoSpringAppConfig.class);
         handler.activateSpringProfiles("override");
-        AwsProxyResponse output = handler.proxy(request, lambdaContext);
+        AwsProxyResponseEvent output = handler.proxy(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
 
         MapResponseModel response = objectMapper.readValue(output.getBody(), MapResponseModel.class);
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/springslowapp/LambdaHandler.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/springslowapp/LambdaHandler.java
index 8b27c9c4f..4b2608e84 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/springslowapp/LambdaHandler.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/springslowapp/LambdaHandler.java
@@ -1,17 +1,17 @@
 package com.amazonaws.serverless.proxy.spring.springslowapp;
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler;
 import com.amazonaws.serverless.proxy.spring.SpringProxyHandlerBuilder;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestHandler;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 
 import java.time.Instant;
 
-public class LambdaHandler implements RequestHandler {
-    private SpringLambdaContainerHandler handler;
+public class LambdaHandler implements RequestHandler {
+    private SpringLambdaContainerHandler handler;
     private long constructorTime;
 
     public LambdaHandler() throws ContainerInitializationException {
@@ -29,7 +29,7 @@ public long getConstructorTime() {
     }
 
     @Override
-    public AwsProxyResponse handleRequest(APIGatewayProxyRequestEvent awsProxyRequest, Context context) {
+    public AwsProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent awsProxyRequest, Context context) {
         return handler.proxy(awsProxyRequest, context);
     }
 }
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/staticapp/LambdaHandler.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/staticapp/LambdaHandler.java
index c565053d3..f40b52a24 100755
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/staticapp/LambdaHandler.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/staticapp/LambdaHandler.java
@@ -2,22 +2,22 @@
 
 
  import com.amazonaws.serverless.exceptions.ContainerInitializationException;
- import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
  import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler;
  import com.amazonaws.services.lambda.runtime.Context;
  import com.amazonaws.services.lambda.runtime.RequestHandler;
 
+ import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
  import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
  import org.springframework.web.context.support.XmlWebApplicationContext;
 
 
  public class LambdaHandler
-   implements RequestHandler
+   implements RequestHandler
  {
-   SpringLambdaContainerHandler handler;
+   SpringLambdaContainerHandler handler;
    boolean isinitialized = false;
  
-   public AwsProxyResponse handleRequest(APIGatewayProxyRequestEvent awsProxyRequest, Context context)
+   public AwsProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent awsProxyRequest, Context context)
    {
         if (!isinitialized) {
             isinitialized = true;
@@ -30,7 +30,7 @@ public AwsProxyResponse handleRequest(APIGatewayProxyRequestEvent awsProxyReques
                 return null;
             }
         }
-        AwsProxyResponse res = handler.proxy(awsProxyRequest, context);
+       AwsProxyResponseEvent res = handler.proxy(awsProxyRequest, context);
         return res;
    }
  }
diff --git a/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootLambdaContainerHandler.java b/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootLambdaContainerHandler.java
index 15c81b9a5..1bff2c329 100644
--- a/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootLambdaContainerHandler.java
+++ b/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootLambdaContainerHandler.java
@@ -16,10 +16,10 @@
 import com.amazonaws.serverless.proxy.*;
 import com.amazonaws.serverless.proxy.internal.servlet.*;
 import com.amazonaws.serverless.proxy.internal.testutils.Timer;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.embedded.ServerlessReactiveServletEmbeddedServerFactory;
 import com.amazonaws.serverless.proxy.spring.embedded.ServerlessServletEmbeddedServerFactory;
 import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent;
 import org.slf4j.Logger;
@@ -76,7 +76,7 @@ public static SpringBootLambdaContainerHandler getInstance() {
      * @return An initialized instance of the `SpringLambdaContainerHandler`
      * @throws ContainerInitializationException If an error occurs while initializing the Spring framework
      */
-    public static SpringBootLambdaContainerHandler getAwsProxyHandler(Class springBootInitializer, String... profiles)
+    public static SpringBootLambdaContainerHandler getAwsProxyHandler(Class springBootInitializer, String... profiles)
             throws ContainerInitializationException {
         return new SpringBootProxyHandlerBuilder()
                 .defaultProxy()
@@ -93,7 +93,7 @@ public static SpringBootLambdaContainerHandler getHttpApiV2ProxyHandler(Class springBootInitializer, String... profiles)
+    public static SpringBootLambdaContainerHandler getHttpApiV2ProxyHandler(Class springBootInitializer, String... profiles)
             throws ContainerInitializationException {
         return new SpringBootProxyHandlerBuilder()
                 .defaultHttpApiV2Proxy()
diff --git a/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootProxyHandlerBuilder.java b/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootProxyHandlerBuilder.java
index cdec18551..698a85b76 100644
--- a/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootProxyHandlerBuilder.java
+++ b/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootProxyHandlerBuilder.java
@@ -14,16 +14,16 @@
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.internal.servlet.ServletLambdaContainerHandlerBuilder;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import org.springframework.boot.WebApplicationType;
 
 import jakarta.servlet.http.HttpServletRequest;
 
 public final class SpringBootProxyHandlerBuilder extends ServletLambdaContainerHandlerBuilder<
         RequestType,
-            AwsProxyResponse,
+            AwsProxyResponseEvent,
             HttpServletRequest,
-            SpringBootLambdaContainerHandler,
+            SpringBootLambdaContainerHandler,
             SpringBootProxyHandlerBuilder> {
     private Class springBootInitializer;
     private String[] profiles;
@@ -51,12 +51,12 @@ public SpringBootProxyHandlerBuilder servletApplication() {
     }
 
     @Override
-    public SpringBootLambdaContainerHandler build() throws ContainerInitializationException {
+    public SpringBootLambdaContainerHandler build() throws ContainerInitializationException {
         validate();
         if (springBootInitializer == null) {
             throw new ContainerInitializationException("Missing spring boot application class in builder", null);
         }
-        SpringBootLambdaContainerHandler handler =  new SpringBootLambdaContainerHandler(
+        SpringBootLambdaContainerHandler handler =  new SpringBootLambdaContainerHandler(
                 requestTypeClass,
                 responseTypeClass,
                 requestReader,
@@ -74,8 +74,8 @@ public SpringBootLambdaContainerHandler build() t
     }
 
     @Override
-    public SpringBootLambdaContainerHandler buildAndInitialize() throws ContainerInitializationException {
-        SpringBootLambdaContainerHandler handler = build();
+    public SpringBootLambdaContainerHandler buildAndInitialize() throws ContainerInitializationException {
+        SpringBootLambdaContainerHandler handler = build();
         initializationWrapper.start(handler);
         return handler;
     }
diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/SecurityAppTest.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/SecurityAppTest.java
index 58ecdb919..eb61bbe15 100644
--- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/SecurityAppTest.java
+++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/SecurityAppTest.java
@@ -2,9 +2,9 @@
 
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.securityapp.LambdaHandler;
 import com.amazonaws.serverless.proxy.spring.securityapp.SecurityConfig;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import org.junit.jupiter.api.Test;
 
@@ -26,7 +26,7 @@ public SecurityAppTest() {
     @Test
     void helloRequest_withAuth_respondsWithSingleMessage() {
         APIGatewayProxyRequestEvent req = new AwsProxyRequestBuilder("/hello", "GET").build();
-        AwsProxyResponse resp = handler.handleRequest(req, lambdaContext);
+        AwsProxyResponseEvent resp = handler.handleRequest(req, lambdaContext);
         assertEquals(401, resp.getStatusCode());
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.WWW_AUTHENTICATE));
         req = new AwsProxyRequestBuilder("/hello", "GET")
diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/ServletAppTest.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/ServletAppTest.java
index 657e86b10..d58bf38b8 100644
--- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/ServletAppTest.java
+++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/ServletAppTest.java
@@ -3,9 +3,9 @@
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.model.ContainerConfig;
 import com.amazonaws.serverless.proxy.spring.servletapp.*;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.MethodSource;
@@ -45,7 +45,7 @@ void asyncRequest(String reqType) {
         AwsProxyRequestBuilder req = new AwsProxyRequestBuilder("/async", "POST")
                 .json()
                 .body("{\"name\":\"bob\"}");
-        AwsProxyResponse resp = handler.handleRequest(req, lambdaContext);
+        AwsProxyResponseEvent resp = handler.handleRequest(req, lambdaContext);
         assertEquals("{\"name\":\"BOB\"}", resp.getBody());
     }
 
@@ -54,7 +54,7 @@ void asyncRequest(String reqType) {
     void helloRequest_respondsWithSingleMessage(String reqType) {
         initServletAppTest(reqType);
         AwsProxyRequestBuilder req = new AwsProxyRequestBuilder("/hello", "GET");
-        AwsProxyResponse resp = handler.handleRequest(req, lambdaContext);
+        AwsProxyResponseEvent resp = handler.handleRequest(req, lambdaContext);
         assertEquals(MessageController.HELLO_MESSAGE, resp.getBody());
     }
 
@@ -67,7 +67,7 @@ void validateRequest_invalidData_respondsWith400(String reqType) {
                 .header(HttpHeaders.ACCEPT, MediaType.TEXT_PLAIN)
                 .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
                 .body(ud);
-        AwsProxyResponse resp = handler.handleRequest(req, lambdaContext);
+        AwsProxyResponseEvent resp = handler.handleRequest(req, lambdaContext);
         try {
             System.out.println(LambdaContainerHandler.getObjectMapper().writeValueAsString(resp));
         } catch (JsonProcessingException e) {
@@ -96,7 +96,7 @@ void messageObject_parsesObject_returnsCorrectMessage(String reqType) {
         AwsProxyRequestBuilder req = new AwsProxyRequestBuilder("/message", "POST")
                 .json()
                 .body(new MessageData("test message"));
-        AwsProxyResponse resp = handler.handleRequest(req, lambdaContext);
+        AwsProxyResponseEvent resp = handler.handleRequest(req, lambdaContext);
         assertNotNull(resp);
         assertEquals(200, resp.getStatusCode());
         assertEquals("test message", resp.getBody());
@@ -110,7 +110,7 @@ void messageObject_propertiesInContentType_returnsCorrectMessage(String reqType)
                 .header(HttpHeaders.CONTENT_TYPE, "application/json;v=1")
                 .header(HttpHeaders.ACCEPT, "application/json;v=1")
                 .body(new MessageData("test message"));
-        AwsProxyResponse resp = handler.handleRequest(req, lambdaContext);
+        AwsProxyResponseEvent resp = handler.handleRequest(req, lambdaContext);
         assertNotNull(resp);
         assertEquals(200, resp.getStatusCode());
         assertEquals("test message", resp.getBody());
@@ -121,7 +121,7 @@ void messageObject_propertiesInContentType_returnsCorrectMessage(String reqType)
     void echoMessage_fileNameLikeParameter_returnsMessage(String reqType) {
         initServletAppTest(reqType);
         AwsProxyRequestBuilder req = new AwsProxyRequestBuilder("/echo/test.test.test", "GET");
-        AwsProxyResponse resp = handler.handleRequest(req, lambdaContext);
+        AwsProxyResponseEvent resp = handler.handleRequest(req, lambdaContext);
         assertNotNull(resp);
         assertEquals(200, resp.getStatusCode());
         assertEquals("test.test.test", resp.getBody());
@@ -136,7 +136,7 @@ void getUtf8String_returnsValidUtf8String(String reqType) {
         LambdaContainerHandler.getContainerConfig().setDefaultContentCharset(ContainerConfig.DEFAULT_CONTENT_CHARSET);
         AwsProxyRequestBuilder req = new AwsProxyRequestBuilder("/content-type/utf8", "GET")
                 .header(HttpHeaders.ACCEPT, MediaType.TEXT_PLAIN);
-        AwsProxyResponse resp = handler.handleRequest(req, lambdaContext);
+        AwsProxyResponseEvent resp = handler.handleRequest(req, lambdaContext);
         assertNotNull(resp);
         assertEquals(200, resp.getStatusCode());
         assertEquals("text/plain; charset=UTF-8", resp.getMultiValueHeaders().get(HttpHeaders.CONTENT_TYPE).stream().collect(Collectors.joining(",")));
@@ -149,7 +149,7 @@ void getUtf8Json_returnsValidUtf8String(String reqType) {
         initServletAppTest(reqType);
         LambdaContainerHandler.getContainerConfig().setDefaultContentCharset(ContainerConfig.DEFAULT_CONTENT_CHARSET);
         AwsProxyRequestBuilder req = new AwsProxyRequestBuilder("/content-type/jsonutf8", "GET");
-        AwsProxyResponse resp = handler.handleRequest(req, lambdaContext);
+        AwsProxyResponseEvent resp = handler.handleRequest(req, lambdaContext);
         assertNotNull(resp);
         assertEquals(200, resp.getStatusCode());
         assertEquals("{\"s\":\"" + MessageController.UTF8_RESPONSE + "\"}", resp.getBody());
@@ -176,7 +176,7 @@ void stream_getUtf8String_returnsValidUtf8String(String reqType) throws IOExcept
         }
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         streamHandler.handleRequest(req, out, lambdaContext);
-        AwsProxyResponse resp = LambdaContainerHandler.getObjectMapper().readValue(out.toByteArray(), AwsProxyResponse.class);
+        AwsProxyResponseEvent resp = LambdaContainerHandler.getObjectMapper().readValue(out.toByteArray(), AwsProxyResponseEvent.class);
         assertNotNull(resp);
         assertEquals(200, resp.getStatusCode());
         assertEquals(MessageController.UTF8_RESPONSE, resp.getBody());
@@ -202,7 +202,7 @@ void stream_getUtf8Json_returnsValidUtf8String(String reqType) throws IOExceptio
         }
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         streamHandler.handleRequest(req, out, lambdaContext);
-        AwsProxyResponse resp = LambdaContainerHandler.getObjectMapper().readValue(out.toByteArray(), AwsProxyResponse.class);
+        AwsProxyResponseEvent resp = LambdaContainerHandler.getObjectMapper().readValue(out.toByteArray(), AwsProxyResponseEvent.class);
         assertNotNull(resp);
         assertEquals(200, resp.getStatusCode());
         assertEquals("{\"s\":\"" + MessageController.UTF8_RESPONSE + "\"}", resp.getBody());
@@ -213,7 +213,7 @@ void stream_getUtf8Json_returnsValidUtf8String(String reqType) throws IOExceptio
     void springExceptionMapping_throw404Ex_expectMappedTo404(String reqType) {
         initServletAppTest(reqType);
         AwsProxyRequestBuilder req = new AwsProxyRequestBuilder("/ex/customstatus", "GET");
-        AwsProxyResponse resp = handler.handleRequest(req, lambdaContext);
+        AwsProxyResponseEvent resp = handler.handleRequest(req, lambdaContext);
         assertNotNull(resp);
         assertEquals(404, resp.getStatusCode());
     }
@@ -226,7 +226,7 @@ void echoMessage_populatesSingleValueHeadersForHttpApiV2(String reqType) {
                 .header(HttpHeaders.CONTENT_TYPE, "application/json;v=1")
                 .header(HttpHeaders.ACCEPT, "application/json;v=1")
                 .body(new MessageData("test message"));
-        AwsProxyResponse resp = handler.handleRequest(req, lambdaContext);
+        AwsProxyResponseEvent resp = handler.handleRequest(req, lambdaContext);
         if ("HTTP_API".equals(type)) {
             assertNotNull(resp.getHeaders());
         } else {
diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/SlowAppTest.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/SlowAppTest.java
index a21486e9c..e940e6e15 100644
--- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/SlowAppTest.java
+++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/SlowAppTest.java
@@ -2,10 +2,10 @@
 
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.slowapp.LambdaHandler;
 import com.amazonaws.serverless.proxy.spring.slowapp.MessageController;
 import com.amazonaws.serverless.proxy.spring.slowapp.SlowTestApplication;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import org.junit.jupiter.api.Test;
 
@@ -23,7 +23,7 @@ void slowAppInit_continuesInBackgroundThread_returnsCorrect() {
         assertTrue(slowApp.getConstructorTime() < 10_000);
         APIGatewayProxyRequestEvent req = new AwsProxyRequestBuilder("/hello", "GET").build();
         long startRequestTime = Instant.now().toEpochMilli();
-        AwsProxyResponse resp = slowApp.handleRequest(req, new MockLambdaContext());
+        AwsProxyResponseEvent resp = slowApp.handleRequest(req, new MockLambdaContext());
         long endRequestTime = Instant.now().toEpochMilli();
         assertTrue(endRequestTime - startRequestTime > SlowTestApplication.SlowDownInit.INIT_SLEEP_TIME_MS - 10_000);
         assertEquals(200, resp.getStatusCode());
diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/WebFluxAppTest.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/WebFluxAppTest.java
index 869e9c3ec..6b1fde406 100644
--- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/WebFluxAppTest.java
+++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/WebFluxAppTest.java
@@ -2,10 +2,10 @@
 
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.webfluxapp.LambdaHandler;
 import com.amazonaws.serverless.proxy.spring.webfluxapp.MessageController;
 import com.amazonaws.serverless.proxy.spring.webfluxapp.MessageData;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.MethodSource;
@@ -37,7 +37,7 @@ public void initWebFluxAppTest(String reqType) {
     void helloRequest_respondsWithSingleMessage(String reqType) {
         initWebFluxAppTest(reqType);
         AwsProxyRequestBuilder req = new AwsProxyRequestBuilder("/single", "GET");
-        AwsProxyResponse resp = handler.handleRequest(req, lambdaContext);
+        AwsProxyResponseEvent resp = handler.handleRequest(req, lambdaContext);
         System.out.println(resp.getBody());
         assertEquals(MessageController.MESSAGE, resp.getBody());
     }
@@ -47,7 +47,7 @@ void helloRequest_respondsWithSingleMessage(String reqType) {
     void helloDoubleRequest_respondsWithDoubleMessage(String reqType) {
         initWebFluxAppTest(reqType);
         AwsProxyRequestBuilder req = new AwsProxyRequestBuilder("/double", "GET");
-        AwsProxyResponse resp = handler.handleRequest(req, lambdaContext);
+        AwsProxyResponseEvent resp = handler.handleRequest(req, lambdaContext);
 
         assertEquals(MessageController.MESSAGE + MessageController.MESSAGE, resp.getBody());
     }
@@ -59,7 +59,7 @@ void messageObject_parsesObject_returnsCorrectMessage(String reqType) throws Jso
         AwsProxyRequestBuilder req = new AwsProxyRequestBuilder("/message", "POST")
                 .json()
                 .body(new MessageData("test message"));
-        AwsProxyResponse resp = handler.handleRequest(req, lambdaContext);
+        AwsProxyResponseEvent resp = handler.handleRequest(req, lambdaContext);
         assertNotNull(resp);
         assertEquals(200, resp.getStatusCode());
         assertEquals("test message", resp.getBody());
diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/embedded/ServerlessServletEmbeddedServerFactoryTest.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/embedded/ServerlessServletEmbeddedServerFactoryTest.java
index e60ebf0c6..7f79f4b60 100644
--- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/embedded/ServerlessServletEmbeddedServerFactoryTest.java
+++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/embedded/ServerlessServletEmbeddedServerFactoryTest.java
@@ -6,8 +6,8 @@
 import com.amazonaws.serverless.proxy.InitializationWrapper;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletRequestReader;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletResponseWriter;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.WebApplicationType;
@@ -19,9 +19,9 @@
 import static org.junit.jupiter.api.Assertions.fail;
 
 public class ServerlessServletEmbeddedServerFactoryTest {
-    private SpringBootLambdaContainerHandler handler = new SpringBootLambdaContainerHandler<>(
+    private SpringBootLambdaContainerHandler handler = new SpringBootLambdaContainerHandler<>(
             APIGatewayProxyRequestEvent.class,
-            AwsProxyResponse.class,
+            AwsProxyResponseEvent.class,
             new AwsProxyHttpServletRequestReader(),
             new AwsProxyHttpServletResponseWriter(),
             new AwsProxySecurityContextWriter(),
diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/securityapp/LambdaHandler.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/securityapp/LambdaHandler.java
index dd41c4307..eceebcdd2 100644
--- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/securityapp/LambdaHandler.java
+++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/securityapp/LambdaHandler.java
@@ -1,14 +1,14 @@
 package com.amazonaws.serverless.proxy.spring.securityapp;
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestHandler;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 
-public class LambdaHandler implements RequestHandler {
-    private static SpringBootLambdaContainerHandler handler;
+public class LambdaHandler implements RequestHandler {
+    private static SpringBootLambdaContainerHandler handler;
 
     static {
         try {
@@ -19,7 +19,7 @@ public class LambdaHandler implements RequestHandler {
-    private static SpringBootLambdaContainerHandler handler;
-    private static SpringBootLambdaContainerHandler httpApiHandler;
-    private static SpringBootLambdaContainerHandler albHandler;
+public class LambdaHandler implements RequestHandler {
+    private static SpringBootLambdaContainerHandler handler;
+    private static SpringBootLambdaContainerHandler httpApiHandler;
+    private static SpringBootLambdaContainerHandler albHandler;
     private String type;
 
     public LambdaHandler(String reqType) {
@@ -53,7 +53,7 @@ public LambdaHandler(String reqType) {
     }
 
     @Override
-    public AwsProxyResponse handleRequest(AwsProxyRequestBuilder awsProxyRequest, Context context) {
+    public AwsProxyResponseEvent handleRequest(AwsProxyRequestBuilder awsProxyRequest, Context context) {
         switch (type) {
             case "API_GW":
                 return handler.proxy(awsProxyRequest.build(), context);
diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/servletapp/LambdaStreamHandler.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/servletapp/LambdaStreamHandler.java
index a826100a2..60e97048e 100644
--- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/servletapp/LambdaStreamHandler.java
+++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/servletapp/LambdaStreamHandler.java
@@ -3,12 +3,12 @@
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.InitializationWrapper;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
 import com.amazonaws.serverless.proxy.spring.SpringBootProxyHandlerBuilder;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
 import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent;
 import jakarta.ws.rs.core.HttpHeaders;
@@ -18,9 +18,9 @@
 import java.io.OutputStream;
 
 public class LambdaStreamHandler implements RequestStreamHandler {
-    private static SpringBootLambdaContainerHandler handler;
-    private static SpringBootLambdaContainerHandler httpApiHandler;
-    private static SpringBootLambdaContainerHandler albHandler;
+    private static SpringBootLambdaContainerHandler handler;
+    private static SpringBootLambdaContainerHandler httpApiHandler;
+    private static SpringBootLambdaContainerHandler albHandler;
     private String type;
 
     public LambdaStreamHandler(String reqType) {
diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/slowapp/LambdaHandler.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/slowapp/LambdaHandler.java
index 748574e37..67df82ce5 100644
--- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/slowapp/LambdaHandler.java
+++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/slowapp/LambdaHandler.java
@@ -1,17 +1,17 @@
 package com.amazonaws.serverless.proxy.spring.slowapp;
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
 import com.amazonaws.serverless.proxy.spring.SpringBootProxyHandlerBuilder;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestHandler;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 
 import java.time.Instant;
 
-public class LambdaHandler implements RequestHandler {
-    private SpringBootLambdaContainerHandler handler;
+public class LambdaHandler implements RequestHandler {
+    private SpringBootLambdaContainerHandler handler;
     private long constructorTime;
 
     public LambdaHandler() {
@@ -34,7 +34,7 @@ public long getConstructorTime() {
     }
 
     @Override
-    public AwsProxyResponse handleRequest(APIGatewayProxyRequestEvent awsProxyRequest, Context context) {
+    public AwsProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent awsProxyRequest, Context context) {
         return handler.proxy(awsProxyRequest, context);
     }
 }
diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/webfluxapp/LambdaHandler.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/webfluxapp/LambdaHandler.java
index 28c232794..854b29491 100644
--- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/webfluxapp/LambdaHandler.java
+++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/webfluxapp/LambdaHandler.java
@@ -3,19 +3,19 @@
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.InitializationWrapper;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
 import com.amazonaws.serverless.proxy.spring.SpringBootProxyHandlerBuilder;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestHandler;
 import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent;
 
-public class LambdaHandler implements RequestHandler {
-    private static SpringBootLambdaContainerHandler handler;
-    private static SpringBootLambdaContainerHandler httpApiHandler;
-    private static SpringBootLambdaContainerHandler albHandler;
+public class LambdaHandler implements RequestHandler {
+    private static SpringBootLambdaContainerHandler handler;
+    private static SpringBootLambdaContainerHandler httpApiHandler;
+    private static SpringBootLambdaContainerHandler albHandler;
 
     private String type;
 
@@ -51,7 +51,7 @@ public LambdaHandler(String reqType) {
     }
 
     @Override
-    public AwsProxyResponse handleRequest(AwsProxyRequestBuilder awsProxyRequest, Context context) {
+    public AwsProxyResponseEvent handleRequest(AwsProxyRequestBuilder awsProxyRequest, Context context) {
         switch (type) {
             case "API_GW":
                 return handler.proxy(awsProxyRequest.build(), context);
diff --git a/aws-serverless-jersey-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java b/aws-serverless-jersey-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java
index 2f19a0282..b2e1a4c42 100644
--- a/aws-serverless-jersey-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java
+++ b/aws-serverless-jersey-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java
@@ -2,7 +2,7 @@
 
 import com.amazonaws.serverless.proxy.jersey.JerseyLambdaContainerHandler;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
 
@@ -28,7 +28,7 @@ public class StreamLambdaHandler implements RequestStreamHandler {
             .property(ServerProperties.MOXY_JSON_FEATURE_DISABLE,true)
             .register(PingResource.class)
             .register(JacksonFeature.class);
-    private static final JerseyLambdaContainerHandler handler
+    private static final JerseyLambdaContainerHandler handler
             = JerseyLambdaContainerHandler.getAwsProxyHandler(jerseyApplication);
 
     @Override
diff --git a/aws-serverless-jersey-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java b/aws-serverless-jersey-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
index ece4f6b37..2db904982 100644
--- a/aws-serverless-jersey-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
+++ b/aws-serverless-jersey-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
@@ -5,7 +5,7 @@
 import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.Context;
 
 import org.junit.jupiter.api.BeforeAll;
@@ -43,11 +43,11 @@ public void ping_streamRequest_respondsWithHello() {
 
         handle(requestStream, responseStream);
 
-        AwsProxyResponse response = readResponse(responseStream);
+        AwsProxyResponseEvent response = readResponse(responseStream);
         assertNotNull(response);
         assertEquals(Response.Status.OK.getStatusCode(), response.getStatusCode());
 
-        assertFalse(response.isBase64Encoded());
+        assertFalse(response.getIsBase64Encoded());
 
         assertTrue(response.getBody().contains("pong"));
         assertTrue(response.getBody().contains("Hello, World!"));
@@ -65,7 +65,7 @@ public void invalidResource_streamRequest_responds404() {
 
         handle(requestStream, responseStream);
 
-        AwsProxyResponse response = readResponse(responseStream);
+        AwsProxyResponseEvent response = readResponse(responseStream);
         assertNotNull(response);
         assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatusCode());
     }
@@ -79,9 +79,9 @@ private void handle(InputStream is, ByteArrayOutputStream os) {
         }
     }
 
-    private AwsProxyResponse readResponse(ByteArrayOutputStream responseStream) {
+    private AwsProxyResponseEvent readResponse(ByteArrayOutputStream responseStream) {
         try {
-            return LambdaContainerHandler.getObjectMapper().readValue(responseStream.toByteArray(), AwsProxyResponse.class);
+            return LambdaContainerHandler.getObjectMapper().readValue(responseStream.toByteArray(), AwsProxyResponseEvent.class);
         } catch (IOException e) {
             e.printStackTrace();
             fail("Error while parsing response: " + e.getMessage());
diff --git a/aws-serverless-spring-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java b/aws-serverless-spring-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java
index 174de1338..2ca47c1e6 100644
--- a/aws-serverless-spring-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java
+++ b/aws-serverless-spring-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java
@@ -3,7 +3,7 @@
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
@@ -14,7 +14,7 @@
 
 
 public class StreamLambdaHandler implements RequestStreamHandler {
-    private static SpringLambdaContainerHandler handler;
+    private static SpringLambdaContainerHandler handler;
     static {
         try {
             handler = SpringLambdaContainerHandler.getAwsProxyHandler(SpringApiConfig.class);
diff --git a/aws-serverless-spring-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java b/aws-serverless-spring-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
index ece4f6b37..2db904982 100644
--- a/aws-serverless-spring-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
+++ b/aws-serverless-spring-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
@@ -5,7 +5,7 @@
 import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.Context;
 
 import org.junit.jupiter.api.BeforeAll;
@@ -43,11 +43,11 @@ public void ping_streamRequest_respondsWithHello() {
 
         handle(requestStream, responseStream);
 
-        AwsProxyResponse response = readResponse(responseStream);
+        AwsProxyResponseEvent response = readResponse(responseStream);
         assertNotNull(response);
         assertEquals(Response.Status.OK.getStatusCode(), response.getStatusCode());
 
-        assertFalse(response.isBase64Encoded());
+        assertFalse(response.getIsBase64Encoded());
 
         assertTrue(response.getBody().contains("pong"));
         assertTrue(response.getBody().contains("Hello, World!"));
@@ -65,7 +65,7 @@ public void invalidResource_streamRequest_responds404() {
 
         handle(requestStream, responseStream);
 
-        AwsProxyResponse response = readResponse(responseStream);
+        AwsProxyResponseEvent response = readResponse(responseStream);
         assertNotNull(response);
         assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatusCode());
     }
@@ -79,9 +79,9 @@ private void handle(InputStream is, ByteArrayOutputStream os) {
         }
     }
 
-    private AwsProxyResponse readResponse(ByteArrayOutputStream responseStream) {
+    private AwsProxyResponseEvent readResponse(ByteArrayOutputStream responseStream) {
         try {
-            return LambdaContainerHandler.getObjectMapper().readValue(responseStream.toByteArray(), AwsProxyResponse.class);
+            return LambdaContainerHandler.getObjectMapper().readValue(responseStream.toByteArray(), AwsProxyResponseEvent.class);
         } catch (IOException e) {
             e.printStackTrace();
             fail("Error while parsing response: " + e.getMessage());
diff --git a/aws-serverless-springboot3-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java b/aws-serverless-springboot3-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java
index e7a93a049..d1b109e08 100644
--- a/aws-serverless-springboot3-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java
+++ b/aws-serverless-springboot3-archetype/src/main/resources/archetype-resources/src/main/java/StreamLambdaHandler.java
@@ -3,7 +3,7 @@
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
@@ -14,7 +14,7 @@
 
 
 public class StreamLambdaHandler implements RequestStreamHandler {
-    private static SpringBootLambdaContainerHandler handler;
+    private static SpringBootLambdaContainerHandler handler;
     static {
         try {
             handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class);
diff --git a/aws-serverless-springboot3-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java b/aws-serverless-springboot3-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
index db68a3fe7..fed28632f 100644
--- a/aws-serverless-springboot3-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
+++ b/aws-serverless-springboot3-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
@@ -3,9 +3,9 @@
 
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.services.lambda.runtime.Context;
 
 import org.junit.jupiter.api.BeforeAll;
@@ -42,11 +42,11 @@ public void ping_streamRequest_respondsWithHello() {
 
         handle(requestStream, responseStream);
 
-        AwsProxyResponse response = readResponse(responseStream);
+        AwsProxyResponseEvent response = readResponse(responseStream);
         assertNotNull(response);
         assertEquals(Response.Status.OK.getStatusCode(), response.getStatusCode());
 
-        assertFalse(response.isBase64Encoded());
+        assertFalse(response.getIsBase64Encoded());
 
         assertTrue(response.getBody().contains("pong"));
         assertTrue(response.getBody().contains("Hello, World!"));
@@ -64,7 +64,7 @@ public void invalidResource_streamRequest_responds404() {
 
         handle(requestStream, responseStream);
 
-        AwsProxyResponse response = readResponse(responseStream);
+        AwsProxyResponseEvent response = readResponse(responseStream);
         assertNotNull(response);
         assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatusCode());
     }
@@ -78,9 +78,9 @@ private void handle(InputStream is, ByteArrayOutputStream os) {
         }
     }
 
-    private AwsProxyResponse readResponse(ByteArrayOutputStream responseStream) {
+    private AwsProxyResponseEvent readResponse(ByteArrayOutputStream responseStream) {
         try {
-            return LambdaContainerHandler.getObjectMapper().readValue(responseStream.toByteArray(), AwsProxyResponse.class);
+            return LambdaContainerHandler.getObjectMapper().readValue(responseStream.toByteArray(), AwsProxyResponseEvent.class);
         } catch (IOException e) {
             e.printStackTrace();
             fail("Error while parsing response: " + e.getMessage());
diff --git a/pom.xml b/pom.xml
index 2ab862554..97b99e1eb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -76,7 +76,7 @@
     
 
     
-        0.6
+        0.7
         8.4.0
         2.15.2
         2.0.9

From 5a0b1b35b4a018ce6f8e14adec0c2b80e193dc86 Mon Sep 17 00:00:00 2001
From: mbfreder 
Date: Wed, 18 Oct 2023 10:36:17 -0700
Subject: [PATCH 6/8] updated samples

---
 .../serverless/sample/jersey/StreamLambdaHandler.java       | 6 +++---
 .../serverless/sample/spring/StreamLambdaHandler.java       | 6 +++---
 .../serverless/sample/springboot3/StreamLambdaHandler.java  | 6 +++---
 3 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/samples/jersey/pet-store/src/main/java/com/amazonaws/serverless/sample/jersey/StreamLambdaHandler.java b/samples/jersey/pet-store/src/main/java/com/amazonaws/serverless/sample/jersey/StreamLambdaHandler.java
index 9701a2c37..2e034e87d 100644
--- a/samples/jersey/pet-store/src/main/java/com/amazonaws/serverless/sample/jersey/StreamLambdaHandler.java
+++ b/samples/jersey/pet-store/src/main/java/com/amazonaws/serverless/sample/jersey/StreamLambdaHandler.java
@@ -4,8 +4,8 @@
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
 import com.amazonaws.serverless.proxy.internal.testutils.Timer;
 import com.amazonaws.serverless.proxy.jersey.JerseyLambdaContainerHandler;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
 
@@ -29,7 +29,7 @@ public class StreamLambdaHandler implements RequestStreamHandler {
             .property(ServerProperties.MOXY_JSON_FEATURE_DISABLE,true)
                                                              .packages("com.amazonaws.serverless.sample.jersey")
                                                              .register(JacksonFeature.class);
-    private static final JerseyLambdaContainerHandler handler
+    private static final JerseyLambdaContainerHandler handler
             = JerseyLambdaContainerHandler.getAwsProxyHandler(jerseyApplication);
 
     public StreamLambdaHandler() {
diff --git a/samples/spring/pet-store/src/main/java/com/amazonaws/serverless/sample/spring/StreamLambdaHandler.java b/samples/spring/pet-store/src/main/java/com/amazonaws/serverless/sample/spring/StreamLambdaHandler.java
index 07675b3fe..e6f26660b 100644
--- a/samples/spring/pet-store/src/main/java/com/amazonaws/serverless/sample/spring/StreamLambdaHandler.java
+++ b/samples/spring/pet-store/src/main/java/com/amazonaws/serverless/sample/spring/StreamLambdaHandler.java
@@ -3,8 +3,8 @@
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.internal.testutils.Timer;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler;
 import com.amazonaws.serverless.sample.spring.filter.CognitoIdentityFilter;
 import com.amazonaws.services.lambda.runtime.Context;
@@ -20,7 +20,7 @@
 
 
 public class StreamLambdaHandler implements RequestStreamHandler {
-    private static SpringLambdaContainerHandler handler;
+    private static SpringLambdaContainerHandler handler;
     static {
         try {
             handler = SpringLambdaContainerHandler.getAwsProxyHandler(PetStoreSpringAppConfig.class);
diff --git a/samples/springboot3/pet-store/src/main/java/com/amazonaws/serverless/sample/springboot3/StreamLambdaHandler.java b/samples/springboot3/pet-store/src/main/java/com/amazonaws/serverless/sample/springboot3/StreamLambdaHandler.java
index 7f329357a..65bd424c2 100644
--- a/samples/springboot3/pet-store/src/main/java/com/amazonaws/serverless/sample/springboot3/StreamLambdaHandler.java
+++ b/samples/springboot3/pet-store/src/main/java/com/amazonaws/serverless/sample/springboot3/StreamLambdaHandler.java
@@ -3,9 +3,9 @@
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.internal.testutils.Timer;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
 import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
+import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.serverless.sample.springboot3.filter.CognitoIdentityFilter;
 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
@@ -20,7 +20,7 @@
 
 
 public class StreamLambdaHandler implements RequestStreamHandler {
-    private static SpringBootLambdaContainerHandler handler;
+    private static SpringBootLambdaContainerHandler handler;
     static {
         try {
             handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class);

From a00d71f5e52d4418711d0c7d443e5da72ecc6997 Mon Sep 17 00:00:00 2001
From: mbfreder 
Date: Wed, 18 Oct 2023 16:24:06 -0700
Subject: [PATCH 7/8] remove helper class

---
 .../servlet/AwsAlbHttpServletRequest.java     | 293 +++++++-----
 .../servlet/AwsHttpServletRequestHelper.java  | 449 ------------------
 .../servlet/AwsProxyHttpServletRequest.java   | 230 ++++++---
 .../proxy/AwsProxyExceptionHandlerTest.java   |  14 +-
 .../testutils/AwsProxyRequestBuilder.java     |  11 +-
 .../proxy/jersey/JerseyAwsProxyTest.java      |  18 +-
 .../proxy/jersey/JerseyParamEncodingTest.java |   8 +-
 .../proxy/spring/SpringAwsProxyTest.java      |  10 +-
 .../spring/SpringServletContextTest.java      |  12 +-
 .../proxy/spring/StaticAppProxyTest.java      |   4 +-
 .../servletapp/LambdaStreamHandler.java       |   2 -
 .../test/java/StreamLambdaHandlerTest.java    |   4 +-
 .../test/java/StreamLambdaHandlerTest.java    |   4 +-
 .../test/java/StreamLambdaHandlerTest.java    |   4 +-
 14 files changed, 364 insertions(+), 699 deletions(-)
 delete mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletRequestHelper.java

diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletRequest.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletRequest.java
index 8319f58cb..6ec99a2cd 100644
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletRequest.java
+++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsAlbHttpServletRequest.java
@@ -8,10 +8,7 @@
 import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import jakarta.servlet.*;
-import jakarta.servlet.http.Cookie;
-import jakarta.servlet.http.HttpServletResponse;
-import jakarta.servlet.http.HttpUpgradeHandler;
-import jakarta.servlet.http.Part;
+import jakarta.servlet.http.*;
 import jakarta.ws.rs.core.HttpHeaders;
 import jakarta.ws.rs.core.SecurityContext;
 import org.slf4j.Logger;
@@ -19,9 +16,15 @@
 
 import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.StringReader;
 import java.io.UnsupportedEncodingException;
 import java.security.Principal;
+import java.time.Instant;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeParseException;
 import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 public class AwsAlbHttpServletRequest extends AwsHttpServletRequest {
 
@@ -54,142 +57,165 @@ public String getAuthType() {
         return securityContext.getAuthenticationScheme();
     }
 
-
     @Override
     public Cookie[] getCookies() {
-        return AwsHttpServletRequestHelper.getCookies(request.getMultiValueHeaders());
+        if (request.getMultiValueHeaders() == null) {
+            return new Cookie[0];
+        }
+        String cookieHeader = getFirst(request.getMultiValueHeaders(), HttpHeaders.COOKIE);
+        if (cookieHeader == null) {
+            return new Cookie[0];
+        }
+        return this.parseCookieHeaderValue(cookieHeader);
     }
 
-
     @Override
     public long getDateHeader(String s) {
-        return AwsHttpServletRequestHelper.getDateHeader(request.getMultiValueHeaders(), s, log);
+        if (request.getMultiValueHeaders() == null) {
+            return -1L;
+        }
+        String dateString = getFirst(request.getMultiValueHeaders(), s);
+        if (dateString == null) {
+            return -1L;
+        }
+        try {
+            return Instant.from(ZonedDateTime.parse(dateString, dateFormatter)).toEpochMilli();
+        } catch (DateTimeParseException e) {
+            log.warn("Invalid date header in request" + SecurityUtils.crlf(dateString));
+            return -1L;
+        }
     }
 
-
     @Override
     public String getHeader(String s) {
-        return AwsHttpServletRequestHelper.getHeader(
-                request.getMultiValueHeaders(),
-                null,
-                null,
-                s,
-                getHeaderValues(s)
-        );
+        List values = getHeaderValues(s);
+        if (values == null || values.size() == 0) {
+            return null;
+        }
+        return values.get(0);
     }
 
-
     @Override
     public Enumeration getHeaders(String s) {
-        return AwsHttpServletRequestHelper.getHeaders(request.getMultiValueHeaders(), s);
+        if (request.getMultiValueHeaders() == null || request.getMultiValueHeaders().get(s) == null) {
+            return Collections.emptyEnumeration();
+        }
+        return Collections.enumeration(request.getMultiValueHeaders().get(s));
     }
 
     @Override
     public Enumeration getHeaderNames() {
-        return AwsHttpServletRequestHelper.getHeaderNames(request.getMultiValueHeaders());
+        if (request.getMultiValueHeaders() == null) {
+            return Collections.emptyEnumeration();
+        }
+        return Collections.enumeration(request.getMultiValueHeaders().keySet());
     }
 
-
     @Override
     public int getIntHeader(String s) {
-        return AwsHttpServletRequestHelper.getIntHeader(request.getMultiValueHeaders(), s);
-    }
+        if (request.getMultiValueHeaders() == null) {
+            return -1;
+        }
+        String headerValue = getFirst(request.getMultiValueHeaders(), s);
+        if (headerValue == null) {
+            return -1;
+        }
 
+        return Integer.parseInt(headerValue);
+    }
 
     @Override
     public String getMethod() {
         return request.getHttpMethod();
     }
 
-
     @Override
     public String getPathInfo() {
-        return AwsHttpServletRequestHelper.getPathInfo(request.getPath());
+        String pathInfo = cleanUri(request.getPath());
+        return decodeRequestPath(pathInfo, LambdaContainerHandler.getContainerConfig());
     }
 
-
     @Override
     public String getPathTranslated() {
-        return AwsHttpServletRequestHelper.getPathTranslated();
+        // Return null because it is an archive on a remote system
+        return null;
     }
 
-
     @Override
     public String getContextPath() {
-        return AwsHttpServletRequestHelper.getContextPath(config, null, this);
+        return generateContextPath(config, null);
     }
 
-
     @Override
     public String getQueryString() {
-        return AwsHttpServletRequestHelper.getQueryString(request.getMultiValueQueryStringParameters(), config, log, this);
+        try {
+            return this.generateQueryString(
+                    request.getMultiValueQueryStringParameters(),
+                    // ALB does not automatically decode parameters, so we don't want to re-encode them
+                    true,
+                    config.getUriEncoding());
+        } catch (ServletException e) {
+            log.error("Could not generate query string", e);
+            return null;
+        }
     }
 
-
     @Override
     public String getRemoteUser() {
-        return AwsHttpServletRequestHelper.getRemoteUser(securityContext);
+        return securityContext.getUserPrincipal().getName();
     }
 
-
     @Override
     public boolean isUserInRole(String s) {
         // TODO: Not supported?
         return false;
     }
 
-
     @Override
     public Principal getUserPrincipal() {
-        return AwsHttpServletRequestHelper.getUserPrincipal(securityContext);
+        return securityContext.getUserPrincipal();
     }
 
-
     @Override
-    public String getRequestURI() { return AwsHttpServletRequestHelper.getRequestURI(request.getPath(), this);}
-
+    public String getRequestURI() {
+        return cleanUri(getContextPath()) + cleanUri(request.getPath());
+    }
 
     @Override
     public StringBuffer getRequestURL() {
-        return AwsHttpServletRequestHelper.getRequestURL(request.getPath(), this);
+        return generateRequestURL(request.getPath());
     }
 
-
     @Override
     public boolean authenticate(HttpServletResponse httpServletResponse)
             throws IOException, ServletException {
         throw new UnsupportedOperationException();
     }
 
-
     @Override
     public void login(String s, String s1)
             throws ServletException {
         throw new UnsupportedOperationException();
     }
 
-
     @Override
     public void logout()
             throws ServletException {
         throw new UnsupportedOperationException();
     }
 
-
     @Override
     public Collection getParts()
             throws IOException, ServletException {
-        return AwsHttpServletRequestHelper.getParts(this);
+        return getMultipartFormParametersMap().values();
     }
 
-
     @Override
     public Part getPart(String s)
             throws IOException, ServletException {
-        return AwsHttpServletRequestHelper.getPart(s, this);
+        return getMultipartFormParametersMap().get(s);
     }
 
-
     @Override
     public  T upgrade(Class aClass)
             throws IOException, ServletException {
@@ -203,14 +229,12 @@ public  T upgrade(Class aClass)
 
     @Override
     public String getCharacterEncoding() {
-        return AwsHttpServletRequestHelper.getCharacterEncoding(
-                request.getMultiValueHeaders(),
-                config,
-                this
-        );
+        if (request.getMultiValueHeaders() == null) {
+            return config.getDefaultContentCharset();
+        }
+        return parseCharacterEncoding(getFirst(request.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE));
     }
 
-
     @Override
     public void setCharacterEncoding(String s)
             throws UnsupportedEncodingException {
@@ -227,105 +251,133 @@ public void setCharacterEncoding(String s)
 
     @Override
     public int getContentLength() {
-        return AwsHttpServletRequestHelper.getContentLength(request.getMultiValueHeaders());
+        String headerValue = getFirst(request.getMultiValueHeaders(), HttpHeaders.CONTENT_LENGTH);
+        if (headerValue == null) {
+            return -1;
+        }
+        return Integer.parseInt(headerValue);
     }
 
-
     @Override
     public long getContentLengthLong() {
-        return AwsHttpServletRequestHelper.getContentLengthLong(request.getMultiValueHeaders());
+        String headerValue = getFirst(request.getMultiValueHeaders(), HttpHeaders.CONTENT_LENGTH);
+        if (headerValue == null) {
+            return -1;
+        }
+        return Long.parseLong(headerValue);
     }
 
-
     @Override
     public String getContentType() {
-        return AwsHttpServletRequestHelper.getContentType(request.getMultiValueHeaders());
+        String contentTypeHeader = getFirst(request.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE);
+        if (contentTypeHeader == null || "".equals(contentTypeHeader.trim())) {
+            return null;
+        }
+
+        return contentTypeHeader;
     }
 
     @Override
     public String getParameter(String s) {
-        return AwsHttpServletRequestHelper.getParameter(
-                request.getMultiValueQueryStringParameters(),
-                s,
-                config,
-                this
-        );
-    }
+        String queryStringParameter = getFirstQueryParamValue(request.getMultiValueQueryStringParameters(), s, config.isQueryStringCaseSensitive());
+        if (queryStringParameter != null) {
+            return queryStringParameter;
+        }
 
+        String[] bodyParams = getFormBodyParameterCaseInsensitive(s);
+        if (bodyParams.length == 0) {
+            return null;
+        } else {
+            return bodyParams[0];
+        }
+    }
 
     @Override
     public Enumeration getParameterNames() {
-        return AwsHttpServletRequestHelper.getParameterNames(
-                request.getMultiValueQueryStringParameters(),
-                this
-        );
+        Set formParameterNames = getFormUrlEncodedParametersMap().keySet();
+        if (request.getMultiValueQueryStringParameters() == null) {
+            return Collections.enumeration(formParameterNames);
+        }
+        return Collections.enumeration(Stream.concat(formParameterNames.stream(),
+                request.getMultiValueQueryStringParameters().keySet().stream()).collect(Collectors.toSet()));
     }
 
-
     @Override
     @SuppressFBWarnings("PZLA_PREFER_ZERO_LENGTH_ARRAYS") // suppressing this as according to the specs we should be returning null here if we can't find params
     public String[] getParameterValues(String s) {
-        return AwsHttpServletRequestHelper.getParameterValues(
-                request.getMultiValueQueryStringParameters(),
-                s,
-                config,
-                this
-        );
-    }
+        List values = new ArrayList<>(Arrays.asList(getQueryParamValues(request.getMultiValueQueryStringParameters(), s, config.isQueryStringCaseSensitive())));
+
+        values.addAll(Arrays.asList(getFormBodyParameterCaseInsensitive(s)));
 
+        if (values.size() == 0) {
+            return null;
+        } else {
+            return values.toArray(new String[0]);
+        }
+    }
 
     @Override
     public Map getParameterMap() {
-        return AwsHttpServletRequestHelper.getParameterMap(
-                request.getMultiValueQueryStringParameters(),
-                config,
-                this
-        );
+        return generateParameterMap(request.getMultiValueQueryStringParameters(), config);
     }
 
-
     @Override
     public String getProtocol() {
         return "";
     }
 
-
     @Override
     public String getScheme() {
-        return AwsHttpServletRequestHelper.getScheme(
-                request.getMultiValueHeaders(),
-                this
-        );
+        return getSchemeFromHeader(request.getMultiValueHeaders());
     }
 
     @Override
     public String getServerName() {
-        return AwsHttpServletRequestHelper.getServerName(
-                request.getMultiValueHeaders(),
-                null
-        );
+        String region = System.getenv("AWS_REGION");
+        if (region == null) {
+            // this is not a critical failure, we just put a static region in the URI
+            region = "us-east-1";
+        }
+
+        if (request.getMultiValueHeaders() != null && request.getMultiValueHeaders().containsKey(HOST_HEADER_NAME)) {
+            String hostHeader = getFirst(request.getMultiValueHeaders(), HOST_HEADER_NAME);
+            if (SecurityUtils.isValidHost(hostHeader, "null", region)) { // ALB doesn't have apiId.
+                return hostHeader;
+            }
+        }
+
+        return new StringBuilder().append("null")
+                .append(".execute-api.")
+                .append(region)
+                .append(".amazonaws.com").toString();
     }
 
     @Override
     public int getServerPort() {
-        return AwsHttpServletRequestHelper.getServerPort(request.getMultiValueHeaders());
+        if (request.getMultiValueHeaders() == null) {
+            return 443;
+        }
+        String port = getFirst(request.getMultiValueHeaders(), PORT_HEADER_NAME);
+        if (SecurityUtils.isValidPort(port)) {
+            return Integer.parseInt(port);
+        } else {
+            return 443; // default port
+        }
     }
 
     @Override
     public ServletInputStream getInputStream() throws IOException {
-        return AwsHttpServletRequestHelper.getInputStream(
-                requestInputStream,
-                request.getBody(),
-                request.getIsBase64Encoded(),
-                this
-        );
+        if (requestInputStream == null) {
+            requestInputStream = new AwsServletInputStream(bodyStringToInputStream(request.getBody(), request.getIsBase64Encoded()));
+        }
+        return requestInputStream;
     }
 
 
     @Override
     public BufferedReader getReader()
             throws IOException {
-        return AwsHttpServletRequestHelper.getReader(request.getBody());
+        return new BufferedReader(new StringReader(request.getBody()));
     }
 
 
@@ -337,36 +389,30 @@ public String getRemoteAddr() {
 
     @Override
     public String getRemoteHost() {
-        return AwsHttpServletRequestHelper.getRemoteHost(request.getMultiValueHeaders());
+        return getFirst(request.getMultiValueHeaders(), HttpHeaders.HOST);
     }
 
-
     @Override
     public Locale getLocale() {
-        return AwsHttpServletRequestHelper.getLocale(
-                request.getMultiValueHeaders(),
-                this
-        );
+        List locales = parseAcceptLanguageHeader(getFirst(request.getMultiValueHeaders(), HttpHeaders.ACCEPT_LANGUAGE));
+        return locales.size() == 0 ? Locale.getDefault() : locales.get(0);
     }
 
     @Override
     public Enumeration getLocales() {
-        return AwsHttpServletRequestHelper.getLocales(
-                request.getMultiValueHeaders(),
-                this
-        );
+        List locales = parseAcceptLanguageHeader(getFirst(request.getMultiValueHeaders(), HttpHeaders.ACCEPT_LANGUAGE));
+        return Collections.enumeration(locales);
     }
 
     @Override
     public boolean isSecure() {
-        return AwsHttpServletRequestHelper.isSecure(securityContext);
+        return securityContext.isSecure();
     }
 
 
-
     @Override
     public RequestDispatcher getRequestDispatcher(String s) {
-        return AwsHttpServletRequestHelper.getRequestDispatcher(s, this);
+        return getServletContext().getRequestDispatcher(s);
     }
 
 
@@ -406,22 +452,19 @@ public AsyncContext startAsync()
     @Override
     public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse)
             throws IllegalStateException {
-        return AwsHttpServletRequestHelper.startAsync(
-                asyncContext,
-                request.getRequestContext().getElb().getTargetGroupArn(),
-                log,
-                servletRequest,
-                servletResponse,
-                containerHandler
-        );
+        servletRequest.setAttribute(DISPATCHER_TYPE_ATTRIBUTE, DispatcherType.ASYNC);
+        asyncContext = new AwsAsyncContext((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse, containerHandler);
+        log.debug("Starting async context for request: " + SecurityUtils.crlf(request.getRequestContext().getElb().getTargetGroupArn()));
+        return asyncContext;
     }
 
     @Override
     public AsyncContext getAsyncContext() {
-        return AwsHttpServletRequestHelper.getAsyncContext(
-                asyncContext,
-                request.getRequestContext().getElb().getTargetGroupArn()
-        );
+        if (asyncContext == null) {
+            throw new IllegalStateException("Request " + SecurityUtils.crlf(request.getRequestContext().getElb().getTargetGroupArn())
+                    + " is not in asynchronous mode. Call startAsync before attempting to get the async context.");
+        }
+        return asyncContext;
     }
 
     @Override
diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletRequestHelper.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletRequestHelper.java
deleted file mode 100644
index e5fdb123e..000000000
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletRequestHelper.java
+++ /dev/null
@@ -1,449 +0,0 @@
-package com.amazonaws.serverless.proxy.internal.servlet;
-
-import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
-import com.amazonaws.serverless.proxy.internal.SecurityUtils;
-import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequest;
-import com.amazonaws.serverless.proxy.model.ContainerConfig;
-import com.amazonaws.serverless.proxy.model.Headers;
-import jakarta.servlet.*;
-import jakarta.servlet.http.Cookie;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import jakarta.servlet.http.Part;
-import jakarta.ws.rs.core.HttpHeaders;
-import jakarta.ws.rs.core.SecurityContext;
-import org.slf4j.Logger;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.StringReader;
-import java.io.UnsupportedEncodingException;
-import java.security.Principal;
-import java.time.Instant;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.time.format.DateTimeParseException;
-import java.util.*;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequest.cleanUri;
-import static com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequest.decodeRequestPath;
-
-public class AwsHttpServletRequestHelper {
-    static final DateTimeFormatter dateFormatter = DateTimeFormatter.RFC_1123_DATE_TIME;
-    private static final String HEADER_KEY_VALUE_SEPARATOR = "=";
-
-    public static Cookie[] getCookies(Map> headers) {
-        if (headers == null) {
-            return new Cookie[0];
-        }
-        String cookieHeader = getFirst(headers, HttpHeaders.COOKIE);
-        if (cookieHeader == null) {
-            return new Cookie[0];
-        }
-        return parseCookieHeaderValue(cookieHeader);
-    }
-
-    public static long getDateHeader(Map> headers, String s, Logger log) {
-        if (headers == null) {
-            return -1L;
-        }
-        String dateString = getFirst(headers, s);
-        if (dateString == null) {
-            return -1L;
-        }
-        try {
-            return Instant.from(ZonedDateTime.parse(dateString, dateFormatter)).toEpochMilli();
-        } catch (DateTimeParseException e) {
-            log.warn("Invalid date header in request" + SecurityUtils.crlf(dateString));
-            return -1L;
-
-        }
-    }
-
-    public static String getHeader(Map> headers, String caller, String userAgent, String s, List headerValues) {
-        List values = headerValues;
-        if (values == null || values.size() == 0) {
-            return null;
-        }
-        return values.get(0);
-    }
-
-    public static Enumeration getHeaders(Map> headers, String s) {
-        if (headers == null || headers.get(s) == null) {
-            return Collections.emptyEnumeration();
-        }
-        return Collections.enumeration(headers.get(s));
-    }
-
-    public static Enumeration getHeaderNames(Map> headers) {
-        if (headers == null) {
-            return Collections.emptyEnumeration();
-        }
-        return Collections.enumeration(headers.keySet());
-    }
-
-    public static int getIntHeader(Map> headers, String s) {
-        if (headers == null) {
-            return -1;
-        }
-        String headerValue = getFirst(headers, s);
-        if (headerValue == null) {
-            return -1;
-        }
-
-        return Integer.parseInt(headerValue);
-    }
-
-    public static String getPathInfo(String path) {
-        String pathInfo = cleanUri(path);
-        return decodeRequestPath(pathInfo, LambdaContainerHandler.getContainerConfig());
-    }
-
-    public static String getPathTranslated() {
-        // Return null because it is an archive on a remote system
-        return null;
-    }
-
-    public static String getContextPath(ContainerConfig config, String stage, AwsHttpServletRequest servletRequest) {
-        return servletRequest.generateContextPath(config, stage);
-    }
-
-    public static String getQueryString(Map> queryStrings, ContainerConfig config, Logger log, AwsHttpServletRequest servletRequest) {
-        try {
-            return servletRequest.generateQueryString(
-                    queryStrings,
-                    // ALB does not automatically decode parameters, so we don't want to re-encode them
-                    true,
-                    config.getUriEncoding());
-        } catch (ServletException e) {
-            log.error("Could not generate query string", e);
-            return null;
-        }
-    }
-
-    public static String getRemoteUser(SecurityContext securityContext) {
-        return securityContext.getUserPrincipal().getName();
-    }
-
-    public static Principal getUserPrincipal(SecurityContext securityContext) {
-        return securityContext.getUserPrincipal();
-    }
-
-    public static String getRequestURI(String path, AwsHttpServletRequest servletRequest) {
-        return cleanUri(servletRequest.getContextPath()) + cleanUri(path);
-    }
-
-    public static StringBuffer getRequestURL(String path, AwsHttpServletRequest servletRequest) {
-        return servletRequest.generateRequestURL(path);
-    }
-
-    public static Collection getParts(AwsHttpServletRequest servletRequest)
-            throws IOException, ServletException {
-        return servletRequest.getMultipartFormParametersMap().values();
-    }
-
-    public static Part getPart(String s, AwsHttpServletRequest servletRequest)
-            throws IOException, ServletException {
-        return servletRequest.getMultipartFormParametersMap().get(s);
-    }
-
-    public static String getCharacterEncoding(Map> headers, ContainerConfig config, AwsHttpServletRequest servletRequest) {
-        if (headers == null) {
-            return config.getDefaultContentCharset();
-        }
-        return servletRequest.parseCharacterEncoding(getFirst(headers, HttpHeaders.CONTENT_TYPE));
-    }
-
-    public static int getContentLength(Map> headers) {
-        String headerValue = getFirst(headers, HttpHeaders.CONTENT_LENGTH);
-        if (headerValue == null) {
-            return -1;
-        }
-        return Integer.parseInt(headerValue);
-    }
-
-    public static long getContentLengthLong(Map> headers) {
-        String headerValue = getFirst(headers, HttpHeaders.CONTENT_LENGTH);
-        if (headerValue == null) {
-            return -1;
-        }
-        return Long.parseLong(headerValue);
-    }
-
-    public static String getContentType(Map> headers) {
-        String contentTypeHeader = getFirst(headers, HttpHeaders.CONTENT_TYPE);
-        if (contentTypeHeader == null || "".equals(contentTypeHeader.trim())) {
-            return null;
-        }
-
-        return contentTypeHeader;
-    }
-
-    public static String getParameter(Map> queryStrings, String s, ContainerConfig config, AwsHttpServletRequest servletRequest) {
-        String queryStringParameter = servletRequest.getFirstQueryParamValue(queryStrings, s, config.isQueryStringCaseSensitive());
-        if (queryStringParameter != null) {
-            return queryStringParameter;
-        }
-
-        String[] bodyParams = servletRequest.getFormBodyParameterCaseInsensitive(s);
-        if (bodyParams.length == 0) {
-            return null;
-        } else {
-            return bodyParams[0];
-        }
-    }
-
-    public static Enumeration getParameterNames(Map> queryStrings, AwsHttpServletRequest servletRequest) {
-        Set formParameterNames = servletRequest.getFormUrlEncodedParametersMap().keySet();
-        if (queryStrings == null) {
-            return Collections.enumeration(formParameterNames);
-        }
-        return Collections.enumeration(Stream.concat(formParameterNames.stream(),
-                queryStrings.keySet().stream()).collect(Collectors.toSet()));
-    }
-
-    public static String[] getParameterValues(Map> queryStrings, String s, ContainerConfig config, AwsHttpServletRequest servletRequest) {
-        List values = new ArrayList<>(Arrays.asList(servletRequest.getQueryParamValues(queryStrings, s, config.isQueryStringCaseSensitive())));
-
-        values.addAll(Arrays.asList(servletRequest.getFormBodyParameterCaseInsensitive(s)));
-
-        if (values.size() == 0) {
-            return null;
-        } else {
-            return values.toArray(new String[0]);
-        }
-    }
-
-    public static Map getParameterMap(Map> queryStrings, ContainerConfig config, AwsHttpServletRequest servletRequest) {
-        return servletRequest.generateParameterMap(queryStrings, config);
-    }
-
-    public static String getScheme(Map> headers, AwsHttpServletRequest servletRequest) {
-        return servletRequest.getSchemeFromHeader(headers);
-    }
-
-    public static String getServerName(Map> headers, String apiId) {
-        String region = System.getenv("AWS_REGION");
-        if (region == null) {
-            // this is not a critical failure, we just put a static region in the URI
-            region = "us-east-1";
-        }
-
-        if (headers != null && headers.containsKey(AwsHttpServletRequest.HOST_HEADER_NAME)) {
-            String hostHeader = getFirst(headers, AwsHttpServletRequest.HOST_HEADER_NAME);
-            if (SecurityUtils.isValidHost(hostHeader, apiId, region)) {
-                return hostHeader;
-            }
-        }
-
-        return new StringBuilder().append(apiId)
-                .append(".execute-api.")
-                .append(region)
-                .append(".amazonaws.com").toString();
-    }
-
-    public static int getServerPort(Map> headers) {
-        if (headers == null) {
-            return 443;
-        }
-        String port = getFirst(headers, AwsHttpServletRequest.PORT_HEADER_NAME);
-        if (SecurityUtils.isValidPort(port)) {
-            return Integer.parseInt(port);
-        } else {
-            return 443; // default port
-        }
-    }
-
-    public static ServletInputStream getInputStream(ServletInputStream requestInputStream, String body, boolean isBase64Encoded, AwsHttpServletRequest servletRequest) throws IOException {
-        if (requestInputStream == null) {
-            requestInputStream = new AwsServletInputStream(servletRequest.bodyStringToInputStream(body, isBase64Encoded));
-        }
-        return requestInputStream;
-    }
-
-    public static BufferedReader getReader(String body)
-            throws IOException {
-        return new BufferedReader(new StringReader(body));
-    }
-
-    public static String getRemoteHost(Map> headers) {
-        return getFirst(headers, HttpHeaders.HOST);
-    }
-
-    public static Locale getLocale(Map> headers, AwsHttpServletRequest servletRequest) {
-        List locales = servletRequest.parseAcceptLanguageHeader(getFirst(headers, HttpHeaders.ACCEPT_LANGUAGE));
-        return locales.size() == 0 ? Locale.getDefault() : locales.get(0);
-    }
-
-    public static Enumeration getLocales(Map> headers, AwsHttpServletRequest servletRequest) {
-        List locales = servletRequest.parseAcceptLanguageHeader(getFirst(headers, HttpHeaders.ACCEPT_LANGUAGE));
-        return Collections.enumeration(locales);
-    }
-
-    public static boolean isSecure(SecurityContext securityContext) {
-        return securityContext.isSecure();
-    }
-
-    public static RequestDispatcher getRequestDispatcher(String s, AwsHttpServletRequest servletRequest) {
-        return servletRequest.getServletContext().getRequestDispatcher(s);
-    }
-
-    public static boolean isAsyncStarted(AwsAsyncContext asyncContext) {
-        if (asyncContext == null) {
-            return false;
-        }
-        if (asyncContext.isCompleted() || asyncContext.isDispatched()) {
-            return false;
-        }
-        return true;
-    }
-
-    public static AsyncContext startAsync(AwsAsyncContext asyncContext, String requestId, Logger log, AwsHttpServletResponse response, AwsLambdaServletContainerHandler containerHandler, AwsHttpServletRequest servletRequest)
-            throws IllegalStateException {
-        asyncContext = new AwsAsyncContext(servletRequest, response, containerHandler);
-        servletRequest.setAttribute(AwsHttpServletRequest.DISPATCHER_TYPE_ATTRIBUTE, DispatcherType.ASYNC);
-        log.debug("Starting async context for request: " + SecurityUtils.crlf(requestId));
-        return asyncContext;
-    }
-
-
-    public static AsyncContext startAsync(AwsAsyncContext asyncContext, String requestId, Logger log, ServletRequest servletRequest, ServletResponse servletResponse, AwsLambdaServletContainerHandler containerHandler)
-            throws IllegalStateException {
-        servletRequest.setAttribute(AwsHttpServletRequest.DISPATCHER_TYPE_ATTRIBUTE, DispatcherType.ASYNC);
-        asyncContext = new AwsAsyncContext((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse, containerHandler);
-        log.debug("Starting async context for request: " + SecurityUtils.crlf(requestId));
-        return asyncContext;
-    }
-
-    public static AsyncContext getAsyncContext(AwsAsyncContext asyncContext, String requestId) {
-        if (asyncContext == null) {
-            throw new IllegalStateException("Request " + SecurityUtils.crlf(requestId)
-                    + " is not in asynchronous mode. Call startAsync before attempting to get the async context.");
-        }
-        return asyncContext;
-    }
-
-
-
-
-    public static String getFirst(Map> map, String key) {
-        List values = map.get(key);
-        if (values == null || values.size() == 0) {
-            return null;
-        }
-        return values.get(0);
-    }
-
-    public static void putSingle(Map> map, String key, String value) {
-        List values = findKey(map, key);
-        values.clear();
-        values.add(value);
-    }
-
-    public static List findKey(Map> map, String key) {
-        List values = map.get(key);
-        if (values == null) {
-            values = new ArrayList<>();
-            map.put(key, values);
-        }
-        return values;
-    }
-
-    /**
-     * Given the Cookie header value, parses it and creates a Cookie object
-     * @param headerValue The string value of the HTTP Cookie header
-     * @return An array of Cookie objects from the header
-     */
-    protected static Cookie[] parseCookieHeaderValue(String headerValue) {
-        List parsedHeaders = parseHeaderValue(headerValue,  ";", ",");
-
-        return parsedHeaders.stream()
-                .filter(e -> e.getKey() != null)
-                .map(e -> new Cookie(SecurityUtils.crlf(e.getKey()), SecurityUtils.crlf(e.getValue())))
-                .toArray(Cookie[]::new);
-    }
-
-    /**
-     * Generic method to parse an HTTP header value and split it into a list of key/values for all its components.
-     * When the property in the header does not specify a key the key field in the output pair is null and only the value
-     * is populated. For example, The header Accept: application/json; application/xml will contain two
-     * key value pairs with key null and the value set to application/json and application/xml respectively.
-     *
-     * @param headerValue The string value for the HTTP header
-     * @param valueSeparator The separator to be used for parsing header values
-     * @return A list of SimpleMapEntry objects with all of the possible values for the header.
-     */
-    protected static List parseHeaderValue(String headerValue, String valueSeparator, String qualifierSeparator) {
-        // Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8
-        // Accept-Language: fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5
-        // Cookie: name=value; name2=value2; name3=value3
-        // X-Custom-Header: YQ==
-
-        List values = new ArrayList<>();
-        if (headerValue == null) {
-            return values;
-        }
-
-        for (String v : headerValue.split(valueSeparator)) {
-            String curValue = v;
-            float curPreference = 1.0f;
-            AwsHttpServletRequest.HeaderValue newValue = new AwsHttpServletRequest.HeaderValue();
-            newValue.setRawValue(v);
-
-            for (String q : curValue.split(qualifierSeparator)) {
-
-                String[] kv = q.split(HEADER_KEY_VALUE_SEPARATOR, 2);
-                String key = null;
-                String val = null;
-                // no separator, set the value only
-                if (kv.length == 1) {
-                    val = q.trim();
-                }
-                // we have a separator
-                if (kv.length == 2) {
-                    // if the length of the value is 0 we assume that we are looking at a
-                    // base64 encoded value with padding so we just set the value. This is because
-                    // we assume that empty values in a key/value pair will contain at least a white space
-                    if (kv[1].length() == 0) {
-                        val = q.trim();
-                    }
-                    // this was a base64 string with an additional = for padding, set the value only
-                    if ("=".equals(kv[1].trim())) {
-                        val = q.trim();
-                    } else { // it's a proper key/value set both
-                        key = kv[0].trim();
-                        val = ("".equals(kv[1].trim()) ? null : kv[1].trim());
-                    }
-                }
-
-                if (newValue.getValue() == null) {
-                    newValue.setKey(key);
-                    newValue.setValue(val);
-                } else {
-                    // special case for quality q=
-                    if ("q".equals(key)) {
-                        curPreference = Float.parseFloat(val);
-                    } else {
-                        newValue.addAttribute(key, val);
-                    }
-                }
-            }
-            newValue.setPriority(curPreference);
-            values.add(newValue);
-        }
-
-        // sort list by preference
-        values.sort((AwsHttpServletRequest.HeaderValue first, AwsHttpServletRequest.HeaderValue second) -> {
-            if ((first.getPriority() - second.getPriority()) < .001f) {
-                return 0;
-            }
-            if (first.getPriority() < second.getPriority()) {
-                return 1;
-            }
-            return -1;
-        });
-        return values;
-    }
-}
diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequest.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequest.java
index 7a59f9831..8a3db1741 100644
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequest.java
+++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequest.java
@@ -33,6 +33,7 @@
 import java.io.StringReader;
 import java.io.UnsupportedEncodingException;
 import java.security.Principal;
+
 import java.time.Instant;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeParseException;
@@ -40,6 +41,7 @@
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+
 /**
  * Implementation of the HttpServletRequest interface that supports APIGatewayProxyRequestEvent object.
  * This object is initialized with an APIGatewayProxyRequestEvent event and a SecurityContext generated
@@ -91,43 +93,73 @@ public String getAuthType() {
 
     @Override
     public Cookie[] getCookies() {
-        return AwsHttpServletRequestHelper.getCookies(request.getMultiValueHeaders());
+        if (request.getMultiValueHeaders() == null) {
+            return new Cookie[0];
+        }
+        String cookieHeader = getFirst(request.getMultiValueHeaders(), HttpHeaders.COOKIE);
+        if (cookieHeader == null) {
+            return new Cookie[0];
+        }
+        return this.parseCookieHeaderValue(cookieHeader);
     }
 
 
     @Override
     public long getDateHeader(String s) {
-        return AwsHttpServletRequestHelper.getDateHeader(request.getMultiValueHeaders(), s, log);
+        if (request.getMultiValueHeaders() == null) {
+            return -1L;
+        }
+        String dateString = getFirst(request.getMultiValueHeaders(), s);
+        if (dateString == null) {
+            return -1L;
+        }
+        try {
+            return Instant.from(ZonedDateTime.parse(dateString, dateFormatter)).toEpochMilli();
+        } catch (DateTimeParseException e) {
+            log.warn("Invalid date header in request" + SecurityUtils.crlf(dateString));
+            return -1L;
+        }
     }
 
-
     @Override
     public String getHeader(String s) {
-        return AwsHttpServletRequestHelper.getHeader(
-                request.getMultiValueHeaders(),
-                request.getRequestContext().getIdentity().getCaller(),
-                request.getRequestContext().getIdentity().getUserAgent(),
-                s,
-                getHeaderValues(s)
-        );
+        List values = getHeaderValues(s);
+        if (values == null || values.size() == 0) {
+            return null;
+        }
+        return values.get(0);
     }
 
 
     @Override
     public Enumeration getHeaders(String s) {
-        return AwsHttpServletRequestHelper.getHeaders(request.getMultiValueHeaders(), s);
+        if (request.getMultiValueHeaders() == null || request.getMultiValueHeaders().get(s) == null) {
+            return Collections.emptyEnumeration();
+        }
+        return Collections.enumeration(request.getMultiValueHeaders().get(s));
     }
 
 
     @Override
     public Enumeration getHeaderNames() {
-        return AwsHttpServletRequestHelper.getHeaderNames(request.getMultiValueHeaders());
+        if (request.getMultiValueHeaders() == null) {
+            return Collections.emptyEnumeration();
+        }
+        return Collections.enumeration(request.getMultiValueHeaders().keySet());
     }
 
 
     @Override
     public int getIntHeader(String s) {
-        return AwsHttpServletRequestHelper.getIntHeader(request.getMultiValueHeaders(), s);
+        if (request.getMultiValueHeaders() == null) {
+            return -1;
+        }
+        String headerValue = getFirst(request.getMultiValueHeaders(), s);
+        if (headerValue == null) {
+            return -1;
+        }
+
+        return Integer.parseInt(headerValue);
     }
 
 
@@ -139,31 +171,42 @@ public String getMethod() {
 
     @Override
     public String getPathInfo() {
-        return AwsHttpServletRequestHelper.getPathInfo(request.getPath());
+        String pathInfo = cleanUri(request.getPath());
+        return decodeRequestPath(pathInfo, LambdaContainerHandler.getContainerConfig());
     }
 
 
     @Override
     public String getPathTranslated() {
-        return AwsHttpServletRequestHelper.getPathTranslated();
+        // Return null because it is an archive on a remote system
+        return null;
     }
 
 
     @Override
     public String getContextPath() {
-        return AwsHttpServletRequestHelper.getContextPath(config, request.getRequestContext().getStage(), this);
+        return generateContextPath(config, request.getRequestContext().getStage());
     }
 
 
     @Override
     public String getQueryString() {
-        return AwsHttpServletRequestHelper.getQueryString(request.getMultiValueQueryStringParameters(), config, log, this);
+        try {
+            return this.generateQueryString(
+                    request.getMultiValueQueryStringParameters(),
+                    // ALB does not automatically decode parameters, so we don't want to re-encode them
+                    true,
+                    config.getUriEncoding());
+        } catch (ServletException e) {
+            log.error("Could not generate query string", e);
+            return null;
+        }
     }
 
 
     @Override
     public String getRemoteUser() {
-        return AwsHttpServletRequestHelper.getRemoteUser(securityContext);
+        return securityContext.getUserPrincipal().getName();
     }
 
 
@@ -176,19 +219,19 @@ public boolean isUserInRole(String s) {
 
     @Override
     public Principal getUserPrincipal() {
-        return AwsHttpServletRequestHelper.getUserPrincipal(securityContext);
+        return securityContext.getUserPrincipal();
     }
 
 
     @Override
     public String getRequestURI() {
-        return AwsHttpServletRequestHelper.getRequestURI(request.getPath(), this);
+        return cleanUri(getContextPath()) + cleanUri(request.getPath());
     }
 
 
     @Override
     public StringBuffer getRequestURL() {
-        return AwsHttpServletRequestHelper.getRequestURL(request.getPath(), this);
+        return generateRequestURL(request.getPath());
     }
 
 
@@ -216,7 +259,7 @@ public void logout()
     @Override
     public Collection getParts()
             throws IOException, ServletException {
-        return AwsHttpServletRequestHelper.getParts(this);
+        return getMultipartFormParametersMap().values();
     }
 
 
@@ -240,11 +283,10 @@ public  T upgrade(Class aClass)
 
     @Override
     public String getCharacterEncoding() {
-        return AwsHttpServletRequestHelper.getCharacterEncoding(
-                request.getMultiValueHeaders(),
-                config,
-                this
-        );
+        if (request.getMultiValueHeaders() == null) {
+            return config.getDefaultContentCharset();
+        }
+        return parseCharacterEncoding(getFirst(request.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE));
     }
 
 
@@ -266,60 +308,78 @@ public void setCharacterEncoding(String s)
 
     @Override
     public int getContentLength() {
-        return AwsHttpServletRequestHelper.getContentLength(request.getMultiValueHeaders());
+        String headerValue = getFirst(request.getMultiValueHeaders(), HttpHeaders.CONTENT_LENGTH);
+        if (headerValue == null) {
+            return -1;
+        }
+        return Integer.parseInt(headerValue);
     }
 
-
     @Override
     public long getContentLengthLong() {
-        return AwsHttpServletRequestHelper.getContentLengthLong(request.getMultiValueHeaders());
+        String headerValue = getFirst(request.getMultiValueHeaders(), HttpHeaders.CONTENT_LENGTH);
+        if (headerValue == null) {
+            return -1;
+        }
+        return Long.parseLong(headerValue);
     }
 
 
     @Override
     public String getContentType() {
-        return AwsHttpServletRequestHelper.getContentType(request.getMultiValueHeaders());
+        String contentTypeHeader = getFirst(request.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE);
+        if (contentTypeHeader == null || "".equals(contentTypeHeader.trim())) {
+            return null;
+        }
+
+        return contentTypeHeader;
     }
 
     @Override
     public String getParameter(String s) {
-        return AwsHttpServletRequestHelper.getParameter(
-                request.getMultiValueQueryStringParameters(),
-                s,
-                config,
-                this
-        );
+        String queryStringParameter = getFirstQueryParamValue(request.getMultiValueQueryStringParameters(), s, config.isQueryStringCaseSensitive());
+        if (queryStringParameter != null) {
+            return queryStringParameter;
+        }
+
+        String[] bodyParams = getFormBodyParameterCaseInsensitive(s);
+        if (bodyParams.length == 0) {
+            return null;
+        } else {
+            return bodyParams[0];
+        }
     }
 
 
     @Override
     public Enumeration getParameterNames() {
-        return AwsHttpServletRequestHelper.getParameterNames(
-                request.getMultiValueQueryStringParameters(),
-                this
-        );
+        Set formParameterNames = getFormUrlEncodedParametersMap().keySet();
+        if (request.getMultiValueQueryStringParameters() == null) {
+            return Collections.enumeration(formParameterNames);
+        }
+        return Collections.enumeration(Stream.concat(formParameterNames.stream(),
+                request.getMultiValueQueryStringParameters().keySet().stream()).collect(Collectors.toSet()));
     }
 
 
     @Override
     @SuppressFBWarnings("PZLA_PREFER_ZERO_LENGTH_ARRAYS") // suppressing this as according to the specs we should be returning null here if we can't find params
     public String[] getParameterValues(String s) {
-        return AwsHttpServletRequestHelper.getParameterValues(
-                request.getMultiValueQueryStringParameters(),
-                s,
-                config,
-                this
-        );
+        List values = new ArrayList<>(Arrays.asList(getQueryParamValues(request.getMultiValueQueryStringParameters(), s, config.isQueryStringCaseSensitive())));
+
+        values.addAll(Arrays.asList(getFormBodyParameterCaseInsensitive(s)));
+
+        if (values.size() == 0) {
+            return null;
+        } else {
+            return values.toArray(new String[0]);
+        }
     }
 
 
     @Override
     public Map getParameterMap() {
-        return AwsHttpServletRequestHelper.getParameterMap(
-                request.getMultiValueQueryStringParameters(),
-                config,
-                this
-        );
+        return generateParameterMap(request.getMultiValueQueryStringParameters(), config);
     }
 
 
@@ -331,40 +391,56 @@ public String getProtocol() {
 
     @Override
     public String getScheme() {
-        return AwsHttpServletRequestHelper.getScheme(
-                request.getMultiValueHeaders(),
-                this
-        );
+        return getSchemeFromHeader(request.getMultiValueHeaders());
     }
 
     @Override
     public String getServerName() {
-        return AwsHttpServletRequestHelper.getServerName(
-                request.getMultiValueHeaders(),
-                request.getRequestContext().getApiId()
-        );
+        String region = System.getenv("AWS_REGION");
+        if (region == null) {
+            // this is not a critical failure, we just put a static region in the URI
+            region = "us-east-1";
+        }
+
+        if (request.getMultiValueHeaders() != null && request.getMultiValueHeaders().containsKey(HOST_HEADER_NAME)) {
+            String hostHeader = getFirst(request.getMultiValueHeaders(), HOST_HEADER_NAME);
+            if (SecurityUtils.isValidHost(hostHeader, request.getRequestContext().getApiId(), region)) {
+                return hostHeader;
+            }
+        }
+
+        return new StringBuilder().append(request.getRequestContext().getApiId())
+                .append(".execute-api.")
+                .append(region)
+                .append(".amazonaws.com").toString();
     }
 
     @Override
     public int getServerPort() {
-        return AwsHttpServletRequestHelper.getServerPort(request.getMultiValueHeaders());
+        if (request.getMultiValueHeaders() == null) {
+            return 443;
+        }
+        String port = getFirst(request.getMultiValueHeaders(), PORT_HEADER_NAME);
+        if (SecurityUtils.isValidPort(port)) {
+            return Integer.parseInt(port);
+        } else {
+            return 443; // default port
+        }
     }
 
     @Override
     public ServletInputStream getInputStream() throws IOException {
-        return AwsHttpServletRequestHelper.getInputStream(
-                requestInputStream,
-                request.getBody(),
-                request.getIsBase64Encoded(),
-                this
-        );
+        if (requestInputStream == null) {
+            requestInputStream = new AwsServletInputStream(bodyStringToInputStream(request.getBody(), request.getIsBase64Encoded()));
+        }
+        return requestInputStream;
     }
 
 
     @Override
     public BufferedReader getReader()
             throws IOException {
-        return AwsHttpServletRequestHelper.getReader(request.getBody());
+        return new BufferedReader(new StringReader(request.getBody()));
     }
 
 
@@ -379,35 +455,31 @@ public String getRemoteAddr() {
 
     @Override
     public String getRemoteHost() {
-        return AwsHttpServletRequestHelper.getRemoteHost(request.getMultiValueHeaders());
+        return getFirst(request.getMultiValueHeaders(), HttpHeaders.HOST);
     }
 
 
     @Override
     public Locale getLocale() {
-        return AwsHttpServletRequestHelper.getLocale(
-                request.getMultiValueHeaders(),
-                this
-        );
+        List locales = parseAcceptLanguageHeader(getFirst(request.getMultiValueHeaders(), HttpHeaders.ACCEPT_LANGUAGE));
+        return locales.size() == 0 ? Locale.getDefault() : locales.get(0);
     }
 
     @Override
     public Enumeration getLocales() {
-        return AwsHttpServletRequestHelper.getLocales(
-                request.getMultiValueHeaders(),
-                this
-        );
+        List locales = parseAcceptLanguageHeader(getFirst(request.getMultiValueHeaders(), HttpHeaders.ACCEPT_LANGUAGE));
+        return Collections.enumeration(locales);
     }
 
     @Override
     public boolean isSecure() {
-        return AwsHttpServletRequestHelper.isSecure(securityContext);
+        return securityContext.isSecure();
     }
 
 
     @Override
     public RequestDispatcher getRequestDispatcher(String s) {
-        return AwsHttpServletRequestHelper.getRequestDispatcher(s, this);
+        return getServletContext().getRequestDispatcher(s);
     }
 
 
diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsProxyExceptionHandlerTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsProxyExceptionHandlerTest.java
index 164fc93ba..00b8373e2 100644
--- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsProxyExceptionHandlerTest.java
+++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsProxyExceptionHandlerTest.java
@@ -3,12 +3,12 @@
 
 import com.amazonaws.serverless.exceptions.InvalidRequestEventException;
 import com.amazonaws.serverless.exceptions.InvalidResponseObjectException;
-import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.serverless.proxy.model.ErrorModel;
 import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 
+import static com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequest.getFirst;
 import static org.junit.jupiter.api.Assertions.*;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
@@ -62,7 +62,7 @@ void typedHandle_InvalidRequestEventException_jsonContentTypeHeader() {
 
         assertNotNull(resp);
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
-        assertEquals(MediaType.APPLICATION_JSON, AwsHttpServletRequestHelper.getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE)); //resp.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE));
+        assertEquals(MediaType.APPLICATION_JSON, getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE)); //resp.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE));
     }
 
     @Test
@@ -89,7 +89,7 @@ void typedHandle_InvalidResponseObjectException_jsonContentTypeHeader() {
 
         assertNotNull(resp);
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
-        assertEquals(MediaType.APPLICATION_JSON, AwsHttpServletRequestHelper.getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE));
+        assertEquals(MediaType.APPLICATION_JSON, getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE));
     }
 
     @Test
@@ -127,7 +127,7 @@ void typedHandle_InternalServerErrorException_jsonContentTypeHeader() {
 
         assertNotNull(resp);
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
-        assertEquals(MediaType.APPLICATION_JSON, AwsHttpServletRequestHelper.getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE));
+        assertEquals(MediaType.APPLICATION_JSON, getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE));
     }
 
     @Test
@@ -138,7 +138,7 @@ void typedHandle_NullPointerException_responseObject()
         assertNotNull(resp);
         assertEquals(502, resp.getStatusCode());
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
-        assertEquals(MediaType.APPLICATION_JSON, AwsHttpServletRequestHelper.getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE));
+        assertEquals(MediaType.APPLICATION_JSON, getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE));
         String body = objectMapper.writeValueAsString(new ErrorModel(AwsProxyExceptionHandler.GATEWAY_TIMEOUT_ERROR));
         assertEquals(body, resp.getBody());
     }
@@ -181,7 +181,7 @@ void streamHandle_InvalidRequestEventException_jsonContentTypeHeader()
         AwsProxyResponseEvent resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponseEvent.class);
         assertNotNull(resp);
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
-        assertEquals(MediaType.APPLICATION_JSON, AwsHttpServletRequestHelper.getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE));
+        assertEquals(MediaType.APPLICATION_JSON, getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE));
     }
 
     @Test
@@ -222,7 +222,7 @@ void streamHandle_InvalidResponseObjectException_jsonContentTypeHeader()
         AwsProxyResponseEvent resp = objectMapper.readValue(new ByteArrayInputStream(respStream.toByteArray()), AwsProxyResponseEvent.class);
         assertNotNull(resp);
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
-        assertEquals(MediaType.APPLICATION_JSON, AwsHttpServletRequestHelper.getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE));
+        assertEquals(MediaType.APPLICATION_JSON, getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE));
     }
 
     @Test
diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java
index 13d178ee1..a491f0650 100644
--- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java
+++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java
@@ -13,6 +13,7 @@
 package com.amazonaws.serverless.proxy.internal.testutils;
 
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
+import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequest;
 import com.amazonaws.serverless.proxy.model.*;
 
 import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent;
@@ -40,7 +41,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.*;
 
-import static com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper.*;
+import static com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequest.getFirst;
 
 
 /**
@@ -330,7 +331,7 @@ public AwsProxyRequestBuilder cookie(String name, String value) {
         }
 
         cookies += (cookies.equals("")?"":"; ") + name + "=" + value;
-        putSingle(request.getMultiValueHeaders(), HttpHeaders.COOKIE, cookies);
+        AwsHttpServletRequest.putSingle(request.getMultiValueHeaders(), HttpHeaders.COOKIE, cookies);
         return this;
     }
 
@@ -338,7 +339,7 @@ public AwsProxyRequestBuilder scheme(String scheme) {
         if (request.getMultiValueHeaders() == null) {
             request.setMultiValueHeaders(new Headers());
         }
-        putSingle(request.getMultiValueHeaders(),"CloudFront-Forwarded-Proto", scheme);
+        AwsHttpServletRequest.putSingle(request.getMultiValueHeaders(),"CloudFront-Forwarded-Proto", scheme);
         return this;
     }
 
@@ -346,7 +347,7 @@ public AwsProxyRequestBuilder serverName(String serverName) {
         if (request.getMultiValueHeaders() == null) {
             request.setMultiValueHeaders(new Headers());
         }
-        putSingle(request.getMultiValueHeaders(), "Host", serverName);
+        AwsHttpServletRequest.putSingle(request.getMultiValueHeaders(), "Host", serverName);
         return this;
     }
 
@@ -379,7 +380,7 @@ public AwsProxyRequestBuilder basicAuth(String username, String password) {
         // we remove the existing authorization strategy
         request.getMultiValueHeaders().remove(HttpHeaders.AUTHORIZATION);
         String authHeader = "Basic " + Base64.getMimeEncoder().encodeToString((username + ":" + password).getBytes(Charset.defaultCharset()));
-        List values = findKey(request.getMultiValueHeaders(), HttpHeaders.AUTHORIZATION);
+        List values = AwsHttpServletRequest.findKey(request.getMultiValueHeaders(), HttpHeaders.AUTHORIZATION);
         values.add(authHeader);
         return this;
     }
diff --git a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyTest.java b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyTest.java
index bdecf23fc..4b432350e 100644
--- a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyTest.java
+++ b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyTest.java
@@ -14,7 +14,6 @@
 
 
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
-import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsServletContext;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
@@ -43,6 +42,7 @@
 import java.util.Collection;
 import java.util.UUID;
 
+import static com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequest.getFirst;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -157,7 +157,7 @@ void headers_getHeaders_echo(String reqType) {
 
         AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
+        assertEquals("application/json", getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
         validateMapResponseModel(output);
     }
@@ -172,7 +172,7 @@ void headers_servletRequest_echo(String reqType) {
 
         AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
+        assertEquals("application/json", getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
         validateMapResponseModel(output);
     }
@@ -188,7 +188,7 @@ void headers_servletRequest_failedDependencyInjection_expectInternalServerError(
         .build();
 
         AwsProxyResponseEvent output = handlerWithoutRegisteredDependencies.proxy(request, lambdaContext);
-        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
+        assertEquals("application/json", getFirst(output.getMultiValueHeaders(), "Content-Type"));
         assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), output.getStatusCode());
     }
 
@@ -211,7 +211,7 @@ void context_serverInfo_correctContext(String reqType) {
         AwsProxyRequestBuilder request = getRequestBuilder("/echo/servlet-context", "GET");
         AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
+        assertEquals("application/json", getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
         validateSingleValueModel(output, AwsServletContext.SERVER_INFO);
     }
@@ -225,7 +225,7 @@ void requestScheme_valid_expectHttps(String reqType) {
 
         AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
+        assertEquals("application/json", getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
         validateSingleValueModel(output, "https");
     }
@@ -239,7 +239,7 @@ void requestFilter_injectsServletRequest_expectCustomAttribute(String reqType) {
 
         AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
+        assertEquals("application/json", getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
         validateSingleValueModel(output, ServletRequestFilter.FILTER_ATTRIBUTE_VALUE);
     }
@@ -255,7 +255,7 @@ void authorizer_securityContext_customPrincipalSuccess(String reqType) {
 
         AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
+        assertEquals("application/json", getFirst(output.getMultiValueHeaders(), "Content-Type"));
         validateSingleValueModel(output, AUTHORIZER_PRINCIPAL_ID);
     }
 
@@ -272,7 +272,7 @@ void authorizer_securityContext_customAuthorizerContextSuccess(String reqType) {
 
         AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
+        assertEquals("application/json", getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
         validateSingleValueModel(output, CUSTOM_HEADER_VALUE);
     }
diff --git a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyParamEncodingTest.java b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyParamEncodingTest.java
index b367813bb..28f237ddf 100644
--- a/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyParamEncodingTest.java
+++ b/aws-serverless-java-container-jersey/src/test/java/com/amazonaws/serverless/proxy/jersey/JerseyParamEncodingTest.java
@@ -2,7 +2,6 @@
 
 
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
-import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
 import com.amazonaws.serverless.proxy.jersey.model.MapResponseModel;
@@ -30,6 +29,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 
+import static com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequest.getFirst;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.fail;
@@ -136,7 +136,7 @@ void queryString_uriInfo_echo(String reqType) {
 
         AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
+        assertEquals("application/json", getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
         validateMapResponseModel(output, QUERY_STRING_KEY, QUERY_STRING_NON_ENCODED_VALUE);
     }
@@ -151,7 +151,7 @@ void queryString_notEncoded_echo(String reqType) {
 
         AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
+        assertEquals("application/json", getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
         validateMapResponseModel(output, QUERY_STRING_KEY, QUERY_STRING_NON_ENCODED_VALUE);
     }
@@ -167,7 +167,7 @@ void queryString_encoded_echo(String reqType) {
 
         AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type"));
+        assertEquals("application/json", getFirst(output.getMultiValueHeaders(), "Content-Type"));
 
         validateMapResponseModel(output, QUERY_STRING_KEY, QUERY_STRING_NON_ENCODED_VALUE);
     }
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringAwsProxyTest.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringAwsProxyTest.java
index db02dd52c..57c8da56e 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringAwsProxyTest.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringAwsProxyTest.java
@@ -2,7 +2,6 @@
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
-import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsLambdaServletContainerHandler;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsServletRegistration;
 import com.amazonaws.serverless.proxy.model.*;
@@ -41,6 +40,7 @@
 import java.util.EnumSet;
 import java.util.UUID;
 
+import static com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequest.getFirst;
 import static org.junit.jupiter.api.Assertions.*;
 import static org.junit.jupiter.api.Assumptions.assumeFalse;
 import static org.junit.jupiter.api.Assumptions.assumeTrue;
@@ -153,7 +153,7 @@ void headers_getHeaders_echo(String reqType) {
 
         AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
+        assertEquals("application/json", getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
         validateMapResponseModel(output);
     }
 
@@ -167,7 +167,7 @@ void headers_servletRequest_echo(String reqType) {
 
         AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
+        assertEquals("application/json", getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
 
         validateMapResponseModel(output);
     }
@@ -182,7 +182,7 @@ void queryString_uriInfo_echo(String reqType) {
 
         AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
+        assertEquals("application/json", getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
 
         validateMapResponseModel(output);
     }
@@ -263,7 +263,7 @@ void authorizer_securityContext_customPrincipalSuccess(String reqType) {
 
         AwsProxyResponseEvent output = executeRequest(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("application/json", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
+        assertEquals("application/json", getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
 
         validateSingleValueModel(output, AUTHORIZER_PRINCIPAL_ID);
     }
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringServletContextTest.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringServletContextTest.java
index 8e2926cb6..b5bc6c1ab 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringServletContextTest.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringServletContextTest.java
@@ -2,7 +2,6 @@
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
-import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.serverless.proxy.internal.servlet.AwsServletContext;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
@@ -23,6 +22,7 @@
 
 import java.util.EnumSet;
 
+import static com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequest.getFirst;
 import static org.junit.jupiter.api.Assertions.*;
 
 // we don't use the spring annotations to pretend we are running in the actual container
@@ -58,7 +58,7 @@ void context_autowireValidContext_echoContext() {
 
         AwsProxyResponseEvent output = handler.proxy(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("text/plain", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
+        assertEquals("text/plain", getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
         assertEquals(STAGE, output.getBody());
     }
 
@@ -71,7 +71,7 @@ void context_contextAware_contextEcho() {
 
         AwsProxyResponseEvent output = handler.proxy(request, lambdaContext);
         assertEquals(200, output.getStatusCode());
-        assertEquals("text/plain", AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
+        assertEquals("text/plain", getFirst(output.getMultiValueHeaders(), "Content-Type").split(";")[0]);
         assertEquals(STAGE, output.getBody());
     }
 
@@ -86,7 +86,7 @@ void filter_customHeaderFilter_echoHeaders() {
         assertNotNull(output.getMultiValueHeaders());
         assertTrue(output.getMultiValueHeaders().size() > 0);
         assertNotNull(output.getMultiValueHeaders().get(CustomHeaderFilter.HEADER_NAME));
-        assertEquals(CustomHeaderFilter.HEADER_VALUE, AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), CustomHeaderFilter.HEADER_NAME));
+        assertEquals(CustomHeaderFilter.HEADER_VALUE, getFirst(output.getMultiValueHeaders(), CustomHeaderFilter.HEADER_NAME));
     }
 
     @Test
@@ -128,8 +128,8 @@ void cookie_injectInResponse_expectCustomSetCookie() {
         assertEquals(200, output.getStatusCode());
         assertTrue(output.getMultiValueHeaders().containsKey(HttpHeaders.SET_COOKIE));
 
-        assertTrue(AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), HttpHeaders.SET_COOKIE).contains(ContextResource.COOKIE_NAME + "=" + ContextResource.COOKIE_VALUE));
-        assertTrue(AwsHttpServletRequestHelper.getFirst(output.getMultiValueHeaders(), HttpHeaders.SET_COOKIE).contains(ContextResource.COOKIE_DOMAIN));
+        assertTrue(getFirst(output.getMultiValueHeaders(), HttpHeaders.SET_COOKIE).contains(ContextResource.COOKIE_NAME + "=" + ContextResource.COOKIE_VALUE));
+        assertTrue(getFirst(output.getMultiValueHeaders(), HttpHeaders.SET_COOKIE).contains(ContextResource.COOKIE_DOMAIN));
     }
 }
 
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/StaticAppProxyTest.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/StaticAppProxyTest.java
index 7af021684..77e7dd5b3 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/StaticAppProxyTest.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/StaticAppProxyTest.java
@@ -2,7 +2,6 @@
 
 
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
-import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
 import com.amazonaws.serverless.proxy.spring.staticapp.LambdaHandler;
@@ -14,6 +13,7 @@
 
 import jakarta.ws.rs.core.MediaType;
 
+import static com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequest.getFirst;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
@@ -32,6 +32,6 @@ void staticPage() {
         assertEquals(200, resp.getStatusCode());
         assertTrue(resp.getBody().startsWith(""));
         assertTrue(resp.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
-        assertEquals(MediaType.TEXT_HTML, AwsHttpServletRequestHelper.getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE));
+        assertEquals(MediaType.TEXT_HTML, getFirst(resp.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE));
     }
 }
diff --git a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/servletapp/LambdaStreamHandler.java b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/servletapp/LambdaStreamHandler.java
index 60e97048e..04be93c7e 100644
--- a/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/servletapp/LambdaStreamHandler.java
+++ b/aws-serverless-java-container-springboot3/src/test/java/com/amazonaws/serverless/proxy/spring/servletapp/LambdaStreamHandler.java
@@ -2,7 +2,6 @@
 
 import com.amazonaws.serverless.exceptions.ContainerInitializationException;
 import com.amazonaws.serverless.proxy.InitializationWrapper;
-import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
 import com.amazonaws.serverless.proxy.spring.SpringBootProxyHandlerBuilder;
 import com.amazonaws.services.lambda.runtime.Context;
@@ -11,7 +10,6 @@
 import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayV2HTTPEvent;
-import jakarta.ws.rs.core.HttpHeaders;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/aws-serverless-jersey-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java b/aws-serverless-jersey-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
index 2db904982..7ddaeff6e 100644
--- a/aws-serverless-jersey-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
+++ b/aws-serverless-jersey-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
@@ -2,7 +2,6 @@
 
 
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
-import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
 import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
@@ -20,6 +19,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 
+import static com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequest.getFirst;
 import static org.junit.jupiter.api.Assertions.*;
 
 
@@ -53,7 +53,7 @@ public void ping_streamRequest_respondsWithHello() {
         assertTrue(response.getBody().contains("Hello, World!"));
 
         assertTrue(response.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
-        assertTrue(AwsHttpServletRequestHelper.getFirst(response.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE).startsWith(MediaType.APPLICATION_JSON));
+        assertTrue(getFirst(response.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE).startsWith(MediaType.APPLICATION_JSON));
     }
 
     @Test
diff --git a/aws-serverless-spring-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java b/aws-serverless-spring-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
index 2db904982..7ddaeff6e 100644
--- a/aws-serverless-spring-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
+++ b/aws-serverless-spring-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
@@ -2,7 +2,6 @@
 
 
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
-import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
 import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
@@ -20,6 +19,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 
+import static com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequest.getFirst;
 import static org.junit.jupiter.api.Assertions.*;
 
 
@@ -53,7 +53,7 @@ public void ping_streamRequest_respondsWithHello() {
         assertTrue(response.getBody().contains("Hello, World!"));
 
         assertTrue(response.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
-        assertTrue(AwsHttpServletRequestHelper.getFirst(response.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE).startsWith(MediaType.APPLICATION_JSON));
+        assertTrue(getFirst(response.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE).startsWith(MediaType.APPLICATION_JSON));
     }
 
     @Test
diff --git a/aws-serverless-springboot3-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java b/aws-serverless-springboot3-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
index fed28632f..1fd4748b5 100644
--- a/aws-serverless-springboot3-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
+++ b/aws-serverless-springboot3-archetype/src/main/resources/archetype-resources/src/test/java/StreamLambdaHandlerTest.java
@@ -2,7 +2,6 @@
 
 
 import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
-import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequestHelper;
 import com.amazonaws.services.lambda.runtime.events.AwsProxyResponseEvent;
 import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
 import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
@@ -20,6 +19,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 
+import static com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletRequest.getFirst;
 import static org.junit.jupiter.api.Assertions.*;
 
 public class StreamLambdaHandlerTest {
@@ -52,7 +52,7 @@ public void ping_streamRequest_respondsWithHello() {
         assertTrue(response.getBody().contains("Hello, World!"));
 
         assertTrue(response.getMultiValueHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
-        assertTrue(AwsHttpServletRequestHelper.getFirst(response.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE).startsWith(MediaType.APPLICATION_JSON));
+        assertTrue(getFirst(response.getMultiValueHeaders(), HttpHeaders.CONTENT_TYPE).startsWith(MediaType.APPLICATION_JSON));
     }
 
     @Test

From 239bc0a3e1f8ca9d31eafa8650dacd3f00cbcded Mon Sep 17 00:00:00 2001
From: mbfreder 
Date: Tue, 24 Oct 2023 15:37:02 -0700
Subject: [PATCH 8/8] Fix samples

---
 .../spring/SpringDelegatingLambdaContainerHandler.java      | 1 -
 .../sample/spring/filter/CognitoIdentityFilter.java         | 6 +++---
 .../sample/springboot3/filter/CognitoIdentityFilter.java    | 6 +++---
 .../sample/springboot3/filter/CognitoIdentityFilter.java    | 6 +++---
 4 files changed, 9 insertions(+), 10 deletions(-)

diff --git a/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringDelegatingLambdaContainerHandler.java b/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringDelegatingLambdaContainerHandler.java
index 75e8dff81..6c7cd7660 100644
--- a/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringDelegatingLambdaContainerHandler.java
+++ b/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringDelegatingLambdaContainerHandler.java
@@ -105,7 +105,6 @@ private HttpServletRequest generateRequest(Map request, Context lambdaContext, S
         httpRequest.setAttribute(RequestReader.API_GATEWAY_CONTEXT_PROPERTY, v1Request.getRequestContext());
         httpRequest.setAttribute(RequestReader.API_GATEWAY_STAGE_VARS_PROPERTY, v1Request.getStageVariables());
         httpRequest.setAttribute(RequestReader.API_GATEWAY_EVENT_PROPERTY, v1Request);
-        //httpRequest.setAttribute(RequestReader.ALB_CONTEXT_PROPERTY, v1Request.getRequestContext().getElb());
         httpRequest.setAttribute(RequestReader.LAMBDA_CONTEXT_PROPERTY, lambdaContext);
         httpRequest.setAttribute(RequestReader.JAX_SECURITY_CONTEXT_PROPERTY, securityWriter.writeSecurityContext(v1Request, lambdaContext));
         return httpRequest;
diff --git a/samples/spring/pet-store/src/main/java/com/amazonaws/serverless/sample/spring/filter/CognitoIdentityFilter.java b/samples/spring/pet-store/src/main/java/com/amazonaws/serverless/sample/spring/filter/CognitoIdentityFilter.java
index ec4242b81..cde10ee11 100644
--- a/samples/spring/pet-store/src/main/java/com/amazonaws/serverless/sample/spring/filter/CognitoIdentityFilter.java
+++ b/samples/spring/pet-store/src/main/java/com/amazonaws/serverless/sample/spring/filter/CognitoIdentityFilter.java
@@ -2,7 +2,7 @@
 
 
 import com.amazonaws.serverless.proxy.RequestReader;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequestContext;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -42,12 +42,12 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
             log.warn("API Gateway context is null");
             filterChain.doFilter(servletRequest, servletResponse);
         }
-        if (!AwsProxyRequestContext.class.isAssignableFrom(apiGwContext.getClass())) {
+        if (!APIGatewayProxyRequestEvent.ProxyRequestContext.class.isAssignableFrom(apiGwContext.getClass())) {
             log.warn("API Gateway context object is not of valid type");
             filterChain.doFilter(servletRequest, servletResponse);
         }
 
-        AwsProxyRequestContext ctx = (AwsProxyRequestContext)apiGwContext;
+        APIGatewayProxyRequestEvent.ProxyRequestContext ctx = (APIGatewayProxyRequestEvent.ProxyRequestContext)apiGwContext;
         if (ctx.getIdentity() == null) {
             log.warn("Identity context is null");
             filterChain.doFilter(servletRequest, servletResponse);
diff --git a/samples/springboot3/alt-pet-store/src/main/java/com/amazonaws/serverless/sample/springboot3/filter/CognitoIdentityFilter.java b/samples/springboot3/alt-pet-store/src/main/java/com/amazonaws/serverless/sample/springboot3/filter/CognitoIdentityFilter.java
index d6ccae765..c8a4b6e63 100644
--- a/samples/springboot3/alt-pet-store/src/main/java/com/amazonaws/serverless/sample/springboot3/filter/CognitoIdentityFilter.java
+++ b/samples/springboot3/alt-pet-store/src/main/java/com/amazonaws/serverless/sample/springboot3/filter/CognitoIdentityFilter.java
@@ -2,7 +2,7 @@
 
 
 import com.amazonaws.serverless.proxy.RequestReader;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequestContext;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -43,12 +43,12 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
             filterChain.doFilter(servletRequest, servletResponse);
             return;
         }
-        if (!AwsProxyRequestContext.class.isAssignableFrom(apiGwContext.getClass())) {
+        if (!APIGatewayProxyRequestEvent.ProxyRequestContext.class.isAssignableFrom(apiGwContext.getClass())) {
             log.warn("API Gateway context object is not of valid type");
             filterChain.doFilter(servletRequest, servletResponse);
         }
 
-        AwsProxyRequestContext ctx = (AwsProxyRequestContext)apiGwContext;
+        APIGatewayProxyRequestEvent.ProxyRequestContext ctx = (APIGatewayProxyRequestEvent.ProxyRequestContext)apiGwContext;
         if (ctx.getIdentity() == null) {
             log.warn("Identity context is null");
             filterChain.doFilter(servletRequest, servletResponse);
diff --git a/samples/springboot3/pet-store/src/main/java/com/amazonaws/serverless/sample/springboot3/filter/CognitoIdentityFilter.java b/samples/springboot3/pet-store/src/main/java/com/amazonaws/serverless/sample/springboot3/filter/CognitoIdentityFilter.java
index d6ccae765..c8a4b6e63 100644
--- a/samples/springboot3/pet-store/src/main/java/com/amazonaws/serverless/sample/springboot3/filter/CognitoIdentityFilter.java
+++ b/samples/springboot3/pet-store/src/main/java/com/amazonaws/serverless/sample/springboot3/filter/CognitoIdentityFilter.java
@@ -2,7 +2,7 @@
 
 
 import com.amazonaws.serverless.proxy.RequestReader;
-import com.amazonaws.serverless.proxy.model.AwsProxyRequestContext;
+import com.amazonaws.services.lambda.runtime.events.apigateway.APIGatewayProxyRequestEvent;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -43,12 +43,12 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
             filterChain.doFilter(servletRequest, servletResponse);
             return;
         }
-        if (!AwsProxyRequestContext.class.isAssignableFrom(apiGwContext.getClass())) {
+        if (!APIGatewayProxyRequestEvent.ProxyRequestContext.class.isAssignableFrom(apiGwContext.getClass())) {
             log.warn("API Gateway context object is not of valid type");
             filterChain.doFilter(servletRequest, servletResponse);
         }
 
-        AwsProxyRequestContext ctx = (AwsProxyRequestContext)apiGwContext;
+        APIGatewayProxyRequestEvent.ProxyRequestContext ctx = (APIGatewayProxyRequestEvent.ProxyRequestContext)apiGwContext;
         if (ctx.getIdentity() == null) {
             log.warn("Identity context is null");
             filterChain.doFilter(servletRequest, servletResponse);