From 434f6ac5dcca2aa7392df46173fa53cee1eec204 Mon Sep 17 00:00:00 2001
From: fred-b
- * Custom authorizers populate the
- * Cognito User Pool authorizers populate the LambdaContainerHandler object that adds protected variables for the
* ServletContext and FilterChainManager. This object should be extended by the framework-specific
* implementations that want to support the servlet 3.1 specs.
+ *
+ * Because Lambda only allows one event per container at a time, this object also acts as the RequestDispatcher
* @param FilterChainManager
- * @param context An initialized ServletContext
+ * Includes a request to the existing framework container. This is called by the AwsProxyRequestDispatcher object
+ * @param servletRequest The modified request object with the new request path
+ * @param servletResponse The original servlet response
+ * @throws ServletException
+ * @throws IOException
*/
- protected void setServletContext(final ServletContext context) {
- servletContext = context;
- // We assume custom implementations of the RequestWriter for HttpServletRequest will reuse
- // the existing AwsServletContext object since it has no dependencies other than the Lambda context
- filterChainManager = new AwsFilterChainManager((AwsServletContext)context);
+ public void include(ContainerRequestType servletRequest, ContainerResponseType servletResponse)
+ throws ServletException, IOException {
+ try {
+ handleRequest(servletRequest, servletResponse, lambdaContext);
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new ServletException(e);
+ }
}
@@ -111,6 +128,43 @@ public void onStartup(final StartupHandler h) {
startupHandler = h;
}
+ @Override
+ protected void handleRequest(ContainerRequestType containerRequest, ContainerResponseType containerResponse, Context lambdaContext)
+ throws Exception {
+ // The servlet context should not be linked to a specific request object, only to the Lambda
+ // context so we only set it once.
+ // TODO: In the future, if we decide to support multiple servlets/contexts in an instance we only need to modify this method
+ if (getServletContext() == null) {
+ setServletContext(new AwsServletContext(lambdaContext, this));
+ }
+ }
+
+
+ //-------------------------------------------------------------
+ // Methods - Getter/Setter
+ //-------------------------------------------------------------
+
+ /**
+ * Returns the current ServletContext. If the framework implementation does not set the value for
+ * servlet context this method will return null.
+ * @return The initialized servlet context if the framework-specific implementation requires one, otherwise null
+ */
+ public ServletContext getServletContext() {
+ return servletContext;
+ }
+
+
+ /**
+ * Sets the ServletContext in the handler and initialized a new FilterChainManager
+ * @param context An initialized ServletContext
+ */
+ protected void setServletContext(final ServletContext context) {
+ servletContext = context;
+ // We assume custom implementations of the RequestWriter for HttpServletRequest will reuse
+ // the existing AwsServletContext object since it has no dependencies other than the Lambda context
+ filterChainManager = new AwsFilterChainManager((AwsServletContext)context);
+ }
+
//-------------------------------------------------------------
// Methods - Protected
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 c11111a7..22658f3d 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
@@ -18,6 +18,7 @@
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
+import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.AsyncContext;
@@ -89,6 +90,10 @@ public AwsProxyHttpServletRequest(AwsProxyRequest awsProxyRequest, Context lambd
this.multipartFormParameters = getMultipartFormParametersMap();
}
+ public AwsProxyRequest getAwsProxyRequest() {
+ return this.request;
+ }
+
//-------------------------------------------------------------
// Implementation - HttpServletRequest
@@ -579,11 +584,11 @@ public boolean isSecure() {
@Override
public RequestDispatcher getRequestDispatcher(String s) {
- return null;
+ return getServletContext().getRequestDispatcher(s);
}
-
@Override
+ @Deprecated
public String getRealPath(String s) {
// we are in an archive on a remote server
return null;
@@ -655,7 +660,7 @@ private MapRequestDispatcher implementation for the AwsProxyHttpServletRequest type. A new
+ * instance of this object is created each time a framework gets the RequestDispatcher from a servlet request. Behind
+ * the scenes, this object uses the AwsLambdaServletContainerHandler to send FORWARD and INCLUDE requests
+ * to the framework.
+ */
+public class AwsProxyRequestDispatcher implements RequestDispatcher {
+
+ //-------------------------------------------------------------
+ // Variables - Private
+ //-------------------------------------------------------------
+
+ private String dispatchPath;
+ private AwsLambdaServletContainerHandler lambdaContainerHandler;
+
+ //-------------------------------------------------------------
+ // Constructors
+ //-------------------------------------------------------------
+
+
+ public AwsProxyRequestDispatcher(final String path, final AwsLambdaServletContainerHandler handler) {
+ if (!path.startsWith("/")) {
+ throw new UnsupportedOperationException("Only dispatchers with absolute paths are supported");
+ }
+
+ dispatchPath = path;
+ lambdaContainerHandler = handler;
+ }
+
+ //-------------------------------------------------------------
+ // Implementation - RequestDispatcher
+ //-------------------------------------------------------------
+
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void forward(ServletRequest servletRequest, ServletResponse servletResponse)
+ throws ServletException, IOException {
+ if (!(servletRequest instanceof AwsProxyHttpServletRequest)) {
+ throw new IOException("Invalid request type: " + servletRequest.getClass().getSimpleName() + ". Only AwsProxyHttpServletRequest is supported");
+ }
+
+ if (lambdaContainerHandler == null) {
+ throw new IOException("Null container handler in dispatcher");
+ }
+
+ ((AwsProxyHttpServletRequest) servletRequest).setDispatcherType(DispatcherType.FORWARD);
+ ((AwsProxyHttpServletRequest) servletRequest).getAwsProxyRequest().setPath(dispatchPath);
+
+ lambdaContainerHandler.forward((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse);
+ }
+
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void include(ServletRequest servletRequest, ServletResponse servletResponse)
+ throws ServletException, IOException {
+ if (!(servletRequest instanceof AwsProxyHttpServletRequest)) {
+ throw new IOException("Invalid request type: " + servletRequest.getClass().getSimpleName() + ". Only AwsProxyHttpServletRequest is supported");
+ }
+
+ if (lambdaContainerHandler == null) {
+ throw new IOException("Null container handler in dispatcher");
+ }
+
+ ((AwsProxyHttpServletRequest) servletRequest).setDispatcherType(DispatcherType.INCLUDE);
+ ((AwsProxyHttpServletRequest) servletRequest).getAwsProxyRequest().setPath(dispatchPath);
+
+ lambdaContainerHandler.include((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse);
+ }
+}
diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsServletContext.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsServletContext.java
index fdadce13..76c4b2eb 100644
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsServletContext.java
+++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsServletContext.java
@@ -67,6 +67,7 @@ public class AwsServletContext
private Context lambdaContext;
private MapRequestReader and ResponseWriter objects.
+ */
+public class ContainerConfig {
+
+ public static ContainerConfig defaultConfig() {
+ ContainerConfig configuration = new ContainerConfig();
+ configuration.setStripBasePath(false);
+
+ return configuration;
+ }
+
+ //-------------------------------------------------------------
+ // Variables - Private
+ //-------------------------------------------------------------
+
+ private String serviceBasePath;
+ private boolean stripBasePath;
+
+
+ //-------------------------------------------------------------
+ // Methods - Getter/Setter
+ //-------------------------------------------------------------
+
+ public String getServiceBasePath() {
+ return serviceBasePath;
+ }
+
+
+ public void setServiceBasePath(String serviceBasePath) {
+ // clean up base path before setting it, we want a "/" at the beginning but not at the end.
+ String finalBasePath = serviceBasePath;
+ if (!finalBasePath.startsWith("/")) {
+ finalBasePath = "/" + serviceBasePath;
+ }
+ if (finalBasePath.endsWith("/")) {
+ finalBasePath = finalBasePath.substring(0, finalBasePath.length() - 1);
+ }
+ this.serviceBasePath = finalBasePath;
+ }
+
+
+ public boolean isStripBasePath() {
+ return stripBasePath;
+ }
+
+
+ public void setStripBasePath(boolean stripBasePath) {
+ this.stripBasePath = stripBasePath;
+ }
+
+
+}
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 22658f3d..5accb191 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
@@ -380,7 +380,7 @@ public String getContentType() {
public ServletInputStream getInputStream() throws IOException {
byte[] bodyBytes = request.getBody().getBytes();
if (request.isBase64Encoded()) {
- bodyBytes = Base64.getDecoder().decode(request.getBody());
+ bodyBytes = Base64.getMimeDecoder().decode(request.getBody());
}
ByteArrayInputStream requestBodyStream = new ByteArrayInputStream(bodyBytes);
return new ServletInputStream() {
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 e5e381b2..469c4ce6 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
@@ -15,6 +15,7 @@
import com.amazonaws.serverless.exceptions.InvalidRequestEventException;
import com.amazonaws.serverless.proxy.internal.RequestReader;
import com.amazonaws.serverless.proxy.internal.model.AwsProxyRequest;
+import com.amazonaws.serverless.proxy.internal.model.ContainerConfig;
import com.amazonaws.services.lambda.runtime.Context;
import javax.ws.rs.core.SecurityContext;
@@ -30,8 +31,9 @@ public class AwsProxyHttpServletRequestReader extends RequestReaderprincipalId field. All other custom values
- * returned by the authorizer are accessible via the getContextValue method.
- * claims
object.
- *
principalId field. All other custom values
+ * returned by the authorizer are accessible via the getContextValue method.
+ *
+ * Cognito User Pool authorizers populate the claims object.
*/
public class ApiGatewayAuthorizerContext {
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 221c6483..32f2fcf7 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
@@ -259,7 +259,7 @@ protected Cookie[] parseCookieHeaderValue(String headerValue) {
/**
* Given a map of key/values query string parameters from API Gateway, creates a query string as it would have
* been in the original url.
- * @param parameters A MapRequestDispatcher
- * @param onStartup method.
- * @param jaxRsApplication A Jersey application instance.
+ * @param requestReader A request reader instance
+ * @param responseWriter A response writer instance
+ * @param securityContextWriter A security context writer object
+ * @param exceptionHandler An exception handler
+ * @param jaxRsApplication The JaxRs application
*/
public JerseyLambdaContainerHandler(RequestReaderclaims object.
*/
+@JsonIgnoreProperties(ignoreUnknown = true)
public class ApiGatewayAuthorizerContext {
//-------------------------------------------------------------
diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/ApiGatewayRequestContext.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/ApiGatewayRequestContext.java
index b91305ac..a3ce1528 100644
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/ApiGatewayRequestContext.java
+++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/ApiGatewayRequestContext.java
@@ -12,6 +12,10 @@
*/
package com.amazonaws.serverless.proxy.internal.model;
+
+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
@@ -20,6 +24,7 @@
* @see AwsProxyRequest
* @see com.amazonaws.serverless.proxy.internal.RequestReader
*/
+@JsonIgnoreProperties(ignoreUnknown = true)
public class ApiGatewayRequestContext {
//-------------------------------------------------------------
diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/ApiGatewayRequestIdentity.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/ApiGatewayRequestIdentity.java
index 206b9439..436cd690 100644
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/ApiGatewayRequestIdentity.java
+++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/ApiGatewayRequestIdentity.java
@@ -12,6 +12,10 @@
*/
package com.amazonaws.serverless.proxy.internal.model;
+
+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 implemnetations
@@ -19,6 +23,7 @@
* @see AwsProxyRequest
* @see com.amazonaws.serverless.proxy.internal.RequestReader
*/
+@JsonIgnoreProperties(ignoreUnknown = true)
public class ApiGatewayRequestIdentity {
//-------------------------------------------------------------
diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/AwsProxyRequest.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/AwsProxyRequest.java
index 39ae6c58..e9b141ec 100644
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/AwsProxyRequest.java
+++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/AwsProxyRequest.java
@@ -13,12 +13,14 @@
package com.amazonaws.serverless.proxy.internal.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import java.util.Map;
/**
* Default implementation of the request object from an API Gateway AWS_PROXY integration
*/
+@JsonIgnoreProperties(ignoreUnknown = true)
public class AwsProxyRequest {
//-------------------------------------------------------------
diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/CognitoAuthorizerClaims.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/CognitoAuthorizerClaims.java
index 552644df..697bce4e 100644
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/CognitoAuthorizerClaims.java
+++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/CognitoAuthorizerClaims.java
@@ -13,6 +13,7 @@
package com.amazonaws.serverless.proxy.internal.model;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.format.DateTimeFormatter;
@@ -36,6 +37,7 @@
* }
*
*/
+@JsonIgnoreProperties(ignoreUnknown = true)
public class CognitoAuthorizerClaims {
//-------------------------------------------------------------
From 0e762c6a886581131f55898d1caa1e9dba1137ee Mon Sep 17 00:00:00 2001
From: Buliani FilterChainHolder
*/
- public FilterChainHolder() {
+ FilterChainHolder() {
this(new ArrayList<>());
}
@@ -52,9 +57,9 @@ public FilterChainHolder() {
* Creates a new instance of a filter chain holder
* @param allFilters A populated list of FilterHolder objects
*/
- public FilterChainHolder(ListfilterCount method to get the filter count
* @return A populated FilterHolder object
*/
- public FilterHolder getFilter(int idx) {
+ FilterHolder getFilter(int idx) {
if (filters == null) {
return null;
} else {
return filters.get(idx);
}
}
+
+
+ /**
+ * Returns the list of filters in this chain.
+ * @return The list of filters
+ */
+ public ListFilterChainHolder object that can be used to apply the filters to the request
*/
- public FilterChainHolder getFilterChain(final HttpServletRequest request) {
+ FilterChainHolder getFilterChain(final HttpServletRequest request) {
String targetPath = request.getServletPath();
DispatcherType type = request.getDispatcherType();
@@ -127,17 +130,22 @@ public FilterChainHolder getFilterChain(final HttpServletRequest request) {
/**
* Retrieves a filter chain from the cache. The cache is lazily loaded as filter chains are requested. If the chain
- * is not available in the cache, the method returns null.
+ * is not available in the cache, the method returns null. This method returns a new instance of FilterChainHolder
+ * initialized with the cached list of {@link FilterHolder} objects
* @param type The dispatcher type for the incoming request
* @param targetPath The request path - this is extracted with the getPath method of the request object
* @return A populated FilterChainHolder
*/
- protected FilterChainHolder getFilterChainCache(final DispatcherType type, final String targetPath) {
+ private FilterChainHolder getFilterChainCache(final DispatcherType type, final String targetPath) {
TargetCacheKey key = new TargetCacheKey();
key.setDispatcherType(type);
key.setTargetPath(targetPath);
- return filterCache.get(key);
+ if (!filterCache.containsKey(key)) {
+ return null;
+ }
+
+ return new FilterChainHolder(filterCache.get(key));
}
@@ -150,7 +158,7 @@ protected FilterChainHolder getFilterChainCache(final DispatcherType type, final
* @param targetPath The target path in the API
* @param holder The FilterChainHolder object to save in the cache
*/
- protected void putFilterChainCache(final DispatcherType type, final String targetPath, final FilterChainHolder holder) {
+ private void putFilterChainCache(final DispatcherType type, final String targetPath, final FilterChainHolder holder) {
TargetCacheKey key = new TargetCacheKey();
key.setDispatcherType(type);
key.setTargetPath(targetPath);
@@ -159,8 +167,8 @@ protected void putFilterChainCache(final DispatcherType type, final String targe
if (key.hashCode() == -1) {
return;
}
+ filterCache.put(key, holder.getFilters());
- filterCache.put(key, holder);
}
@@ -171,7 +179,7 @@ protected void putFilterChainCache(final DispatcherType type, final String targe
* @param mapping The mapping path stored in the filter registration
* @return true if the given mapping path can apply to the target, false otherwise.
*/
- protected boolean pathMatches(final String target, final String mapping) {
+ boolean pathMatches(final String target, final String mapping) {
// easiest case, they are exactly the same
if (target.toLowerCase().equals(mapping.toLowerCase())) {
return true;
@@ -275,11 +283,7 @@ public int hashCode() {
@Override
public boolean equals(Object key) {
- if (!key.getClass().isAssignableFrom(TargetCacheKey.class)) {
- return false;
- } else {
- return hashCode() == key.hashCode();
- }
+ return key.getClass().isAssignableFrom(TargetCacheKey.class) && hashCode() == key.hashCode();
}
@@ -287,22 +291,12 @@ public boolean equals(Object key) {
// Methods - Getter/Setter
//-------------------------------------------------------------
- public String getTargetPath() {
- return targetPath;
- }
-
-
- public void setTargetPath(String targetPath) {
+ void setTargetPath(String targetPath) {
this.targetPath = targetPath;
}
- public DispatcherType getDispatcherType() {
- return dispatcherType;
- }
-
-
- public void setDispatcherType(DispatcherType dispatcherType) {
+ void setDispatcherType(DispatcherType dispatcherType) {
this.dispatcherType = dispatcherType;
}
}
diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsFilterChainManagerTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsFilterChainManagerTest.java
index 5b2a1aa4..84f320b9 100644
--- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsFilterChainManagerTest.java
+++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsFilterChainManagerTest.java
@@ -5,20 +5,27 @@
import com.amazonaws.services.lambda.runtime.Context;
import org.junit.BeforeClass;
import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import javax.servlet.*;
import java.io.IOException;
import java.util.EnumSet;
+import java.util.concurrent.CountDownLatch;
import static org.junit.Assert.*;
public class AwsFilterChainManagerTest {
+ private static final String REQUEST_CUSTOM_ATTRIBUTE_NAME = "X-Custom-Attribute";
+ private static final String REQUEST_CUSTOM_ATTRIBUTE_VALUE = "CustomAttrValue";
private static AwsFilterChainManager chainManager;
private static Context lambdaContext = new MockLambdaContext();
private static ServletContext servletContext;
+ private Logger log = LoggerFactory.getLogger(AwsFilterChainManagerTest.class);
+
@BeforeClass
public static void setUp() {
servletContext = new AwsServletContext( null);//AwsServletContext.getInstance(lambdaContext, null);
@@ -127,6 +134,76 @@ public void filterChain_getFilterChain_subsetOfFilters() {
assertEquals("Filter2", fcHolder.getFilter(0).getFilterName());
}
+ @Test
+ public void filterChain_matchMultipleTimes_expectSameMatch() {
+ AwsProxyHttpServletRequest req = new AwsProxyHttpServletRequest(
+ new AwsProxyRequestBuilder("/first/second", "GET").build(), lambdaContext, null
+ );
+ req.setServletContext(servletContext);
+ FilterChainHolder fcHolder = chainManager.getFilterChain(req);
+ assertEquals(1, fcHolder.filterCount());
+ assertEquals("Filter1", fcHolder.getFilter(0).getFilterName());
+
+ AwsProxyHttpServletRequest req2 = new AwsProxyHttpServletRequest(
+ new AwsProxyRequestBuilder("/first/second", "GET").build(), lambdaContext, null
+ );
+ req.setServletContext(servletContext);
+ FilterChainHolder fcHolder2 = chainManager.getFilterChain(req2);
+ assertEquals(1, fcHolder2.filterCount());
+ assertEquals("Filter1", fcHolder2.getFilter(0).getFilterName());
+ }
+
+ @Test
+ public void filerChain_executeMultipleFilters_expectRunEachTime() {
+ AwsProxyHttpServletRequest req = new AwsProxyHttpServletRequest(
+ new AwsProxyRequestBuilder("/first/second", "GET").build(), lambdaContext, null
+ );
+ req.setServletContext(servletContext);
+ FilterChainHolder fcHolder = chainManager.getFilterChain(req);
+ assertEquals(1, fcHolder.filterCount());
+ assertEquals("Filter1", fcHolder.getFilter(0).getFilterName());
+ AwsHttpServletResponse resp = new AwsHttpServletResponse(req, new CountDownLatch(1));
+
+ try {
+ fcHolder.doFilter(req, resp);
+ } catch (IOException e) {
+ fail("IO Exception while executing filters");
+ e.printStackTrace();
+ } catch (ServletException e) {
+ fail("Servlet exception while executing filters");
+ e.printStackTrace();
+ }
+
+ assertTrue(req.getAttribute(REQUEST_CUSTOM_ATTRIBUTE_NAME) != null);
+ assertEquals(REQUEST_CUSTOM_ATTRIBUTE_VALUE, req.getAttribute(REQUEST_CUSTOM_ATTRIBUTE_NAME));
+
+ log.debug("Starting second request");
+
+ AwsProxyHttpServletRequest req2 = new AwsProxyHttpServletRequest(
+ new AwsProxyRequestBuilder("/first/second", "GET").build(), lambdaContext, null
+ );
+ req2.setServletContext(servletContext);
+ FilterChainHolder fcHolder2 = chainManager.getFilterChain(req2);
+ assertEquals(1, fcHolder2.filterCount());
+ assertEquals("Filter1", fcHolder2.getFilter(0).getFilterName());
+ assertEquals(-1, fcHolder2.currentFilter);
+
+ AwsHttpServletResponse resp2 = new AwsHttpServletResponse(req, new CountDownLatch(1));
+
+ try {
+ fcHolder2.doFilter(req2, resp2);
+ } catch (IOException e) {
+ fail("IO Exception while executing filters");
+ e.printStackTrace();
+ } catch (ServletException e) {
+ fail("Servlet exception while executing filters");
+ e.printStackTrace();
+ }
+
+ assertTrue(req2.getAttribute(REQUEST_CUSTOM_ATTRIBUTE_NAME) != null);
+ assertEquals(REQUEST_CUSTOM_ATTRIBUTE_VALUE, req2.getAttribute(REQUEST_CUSTOM_ATTRIBUTE_NAME));
+ }
+
@Test
public void filterChain_getFilterChain_multipleFilters() {
AwsProxyHttpServletRequest req = new AwsProxyHttpServletRequest(
@@ -159,6 +236,7 @@ public void init(FilterConfig filterConfig) throws ServletException {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("DoFilter");
+ servletRequest.setAttribute(REQUEST_CUSTOM_ATTRIBUTE_NAME, REQUEST_CUSTOM_ATTRIBUTE_VALUE);
filterChain.doFilter(servletRequest, servletResponse);
}
diff --git a/aws-serverless-java-container-spark/pom.xml b/aws-serverless-java-container-spark/pom.xml
index 9663e776..d93fc032 100644
--- a/aws-serverless-java-container-spark/pom.xml
+++ b/aws-serverless-java-container-spark/pom.xml
@@ -31,6 +31,12 @@
RequestReader and ResponseWriter objects.
*/
public class ContainerConfig {
+ public static final String DEFAULT_URI_ENCODING = "UTF-8";
public static ContainerConfig defaultConfig() {
ContainerConfig configuration = new ContainerConfig();
configuration.setStripBasePath(false);
+ configuration.setUriEncoding(DEFAULT_URI_ENCODING);
return configuration;
}
@@ -19,6 +21,7 @@ public static ContainerConfig defaultConfig() {
private String serviceBasePath;
private boolean stripBasePath;
+ private String uriEncoding;
//-------------------------------------------------------------
@@ -53,4 +56,12 @@ public void setStripBasePath(boolean stripBasePath) {
}
+ public String getUriEncoding() {
+ return uriEncoding;
+ }
+
+
+ public void setUriEncoding(String uriEncoding) {
+ this.uriEncoding = uriEncoding;
+ }
}
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 81dc21ff..15ad74da 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
@@ -12,6 +12,7 @@
*/
package com.amazonaws.serverless.proxy.internal.servlet;
+import com.amazonaws.serverless.proxy.internal.model.ContainerConfig;
import com.amazonaws.services.lambda.runtime.Context;
import org.slf4j.Logger;
@@ -24,6 +25,7 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.AbstractMap;
@@ -320,4 +322,15 @@ protected ListRequestReader and ResponseWriter objects.
+ * Configuration parameters for the framework
*/
public class ContainerConfig {
public static final String DEFAULT_URI_ENCODING = "UTF-8";
@@ -11,6 +11,7 @@ public static ContainerConfig defaultConfig() {
ContainerConfig configuration = new ContainerConfig();
configuration.setStripBasePath(false);
configuration.setUriEncoding(DEFAULT_URI_ENCODING);
+ configuration.setConsolidateSetCookieHeaders(true);
return configuration;
}
@@ -22,6 +23,7 @@ public static ContainerConfig defaultConfig() {
private String serviceBasePath;
private boolean stripBasePath;
private String uriEncoding;
+ private boolean consolidateSetCookieHeaders;
//-------------------------------------------------------------
@@ -33,6 +35,11 @@ public String getServiceBasePath() {
}
+ /**
+ * Configures a base path that can be strippped from the request path before passing it to the frameowkr-specific implementation. This can be used to
+ * remove API Gateway's base path mappings from the request.
+ * @param serviceBasePath The base path mapping to be removed.
+ */
public void setServiceBasePath(String serviceBasePath) {
// clean up base path before setting it, we want a "/" at the beginning but not at the end.
String finalBasePath = serviceBasePath;
@@ -51,6 +58,11 @@ public boolean isStripBasePath() {
}
+ /**
+ * Whether this framework should strip the base path mapping specified with the {@link #setServiceBasePath(String)} method from a request before
+ * passing it to the framework-specific implementations
+ * @param stripBasePath
+ */
public void setStripBasePath(boolean stripBasePath) {
this.stripBasePath = stripBasePath;
}
@@ -61,7 +73,30 @@ public String getUriEncoding() {
}
+ /**
+ * Sets the charset used to URLEncode and Decode request paths.
+ * @param uriEncoding The charset. By default this is set to UTF-8
+ */
public void setUriEncoding(String uriEncoding) {
this.uriEncoding = uriEncoding;
}
+
+
+ public boolean isConsolidateSetCookieHeaders() {
+ return consolidateSetCookieHeaders;
+ }
+
+
+ /**
+ * Tells the library to consolidate multiple Set-Cookie headers into a single Set-Cookie header with multiple, comma-separated values. This is allowed
+ * by the RFC 2109 (https://tools.ietf.org/html/rfc2109). However, since not all clients support this, we consider it optional. When this value is set
+ * to true the framework will consolidate all Set-Cookie headers into a single header, when it's set to false, the framework will only return the first
+ * Set-Cookie header specified in a response.
+ *
+ * Because API Gateway needs header keys to be unique, we give an option to configure this.
+ * @param consolidateSetCookieHeaders Whether to consolidate the cookie headers or not.
+ */
+ public void setConsolidateSetCookieHeaders(boolean consolidateSetCookieHeaders) {
+ this.consolidateSetCookieHeaders = consolidateSetCookieHeaders;
+ }
}
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 af99fd3e..126f7adb 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
@@ -12,6 +12,8 @@
*/
package com.amazonaws.serverless.proxy.internal.servlet;
+import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -410,7 +412,22 @@ byte[] getAwsResponseBodyBytes() {
MapFilterChainHolder
+ * @param servlet
*/
- FilterChainHolder() {
- this(new ArrayList<>());
+ FilterChainHolder(Servlet servlet) {
+ this(new ArrayList<>(), servlet);
}
/**
* Creates a new instance of a filter chain holder
* @param allFilters A populated list of FilterHolder objects
+ * @param servlet
*/
- FilterChainHolder(ListgetPath method of the request object
+ * @param servlet Servlet to put at the end of the chain (optional).
* @return A populated FilterChainHolder
*/
- private FilterChainHolder getFilterChainCache(final DispatcherType type, final String targetPath) {
+ private FilterChainHolder getFilterChainCache(final DispatcherType type, final String targetPath, Servlet servlet) {
TargetCacheKey key = new TargetCacheKey();
key.setDispatcherType(type);
key.setTargetPath(targetPath);
@@ -145,7 +148,7 @@ private FilterChainHolder getFilterChainCache(final DispatcherType type, final S
return null;
}
- return new FilterChainHolder(filterCache.get(key));
+ return new FilterChainHolder(filterCache.get(key), servlet);
}
diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/FilterHolder.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/FilterHolder.java
index daa31828..a8446b14 100644
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/FilterHolder.java
+++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/FilterHolder.java
@@ -29,7 +29,7 @@ public class FilterHolder {
//-------------------------------------------------------------
private Filter filter;
- private FilterConfig filterConfig;
+ private FilterConfig filterConfig = new Config();
private Registration registration;
private String filterName;
private MapFilterChainHolder
- * @param servlet
*/
- FilterChainHolder(Servlet servlet) {
- this(new ArrayList<>(), servlet);
+ FilterChainHolder() {
+ this(new ArrayList<>());
}
/**
* Creates a new instance of a filter chain holder
* @param allFilters A populated list of FilterHolder objects
- * @param servlet
*/
- FilterChainHolder(ListLambdaFlushResponseListener in your SpringBootServletInitializer subclass configure().
*
* @param RequestReader
*
- * @see com.amazonaws.serverless.proxy.internal.RequestReader
+ * @see RequestReader
*/
public class ContainerInitializationException extends Exception {
public ContainerInitializationException(String message, Exception e) {
diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/exceptions/InvalidRequestEventException.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/exceptions/InvalidRequestEventException.java
index faf82b9c..bf42df94 100644
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/exceptions/InvalidRequestEventException.java
+++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/exceptions/InvalidRequestEventException.java
@@ -12,11 +12,15 @@
*/
package com.amazonaws.serverless.exceptions;
+
+import com.amazonaws.serverless.proxy.RequestReader;
+
+
/**
* This exception is thrown when the ContainerHandler fails to parse a request object or input stream into the
* object required by the Container. The exception is thrown by implementing sub-classes of RequestReader
*
- * @see com.amazonaws.serverless.proxy.internal.RequestReader
+ * @see RequestReader
*/
public class InvalidRequestEventException extends Exception {
public InvalidRequestEventException(String message, Exception e) {
diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/exceptions/InvalidResponseObjectException.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/exceptions/InvalidResponseObjectException.java
index 33189ad4..b3ac9c1e 100644
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/exceptions/InvalidResponseObjectException.java
+++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/exceptions/InvalidResponseObjectException.java
@@ -12,11 +12,15 @@
*/
package com.amazonaws.serverless.exceptions;
+
+import com.amazonaws.serverless.proxy.ResponseWriter;
+
+
/**
* This exception is thrown when the ContainerHandler cannot transform the Container response into a valid return value
* for the Lambda function. This exception is thrown by implementing sub-classes of ResponseWriter
*
- * @see com.amazonaws.serverless.proxy.internal.ResponseWriter
+ * @see ResponseWriter
*/
public class InvalidResponseObjectException extends Exception {
public InvalidResponseObjectException(String message, Exception e) {
diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/AwsProxySecurityContextWriter.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/AwsProxySecurityContextWriter.java
similarity index 94%
rename from aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/AwsProxySecurityContextWriter.java
rename to aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/AwsProxySecurityContextWriter.java
index 839658ff..9a38bbf2 100644
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/AwsProxySecurityContextWriter.java
+++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/AwsProxySecurityContextWriter.java
@@ -10,10 +10,10 @@
* 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.internal;
+package com.amazonaws.serverless.proxy;
import com.amazonaws.serverless.proxy.internal.jaxrs.AwsProxySecurityContext;
-import com.amazonaws.serverless.proxy.internal.model.AwsProxyRequest;
+import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
import com.amazonaws.services.lambda.runtime.Context;
import javax.ws.rs.core.SecurityContext;
diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/ExceptionHandler.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/ExceptionHandler.java
similarity index 98%
rename from aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/ExceptionHandler.java
rename to aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/ExceptionHandler.java
index a56e2ec7..846c6478 100644
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/ExceptionHandler.java
+++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/ExceptionHandler.java
@@ -10,7 +10,7 @@
* 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.internal;
+package com.amazonaws.serverless.proxy;
import java.io.IOException;
import java.io.OutputStream;
diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/RequestReader.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/RequestReader.java
similarity index 90%
rename from aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/RequestReader.java
rename to aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/RequestReader.java
index fcd7dacf..1a63aec8 100644
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/RequestReader.java
+++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/RequestReader.java
@@ -10,20 +10,15 @@
* 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.internal;
+package com.amazonaws.serverless.proxy;
import com.amazonaws.serverless.exceptions.InvalidRequestEventException;
-import com.amazonaws.serverless.proxy.internal.model.ContainerConfig;
+import com.amazonaws.serverless.proxy.model.ContainerConfig;
import com.amazonaws.services.lambda.runtime.Context;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
import javax.ws.rs.core.SecurityContext;
-import java.io.IOException;
-import java.io.InputStream;
-
/**
* Implementations of the RequestReader object are used by container objects to transform the incoming Lambda event into
@@ -63,13 +58,13 @@ public abstract class RequestReaderAwsProxyExceptionHandler objcect.
diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/AwsProxySecurityContextWriterTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsProxySecurityContextWriterTest.java
similarity index 87%
rename from aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/AwsProxySecurityContextWriterTest.java
rename to aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsProxySecurityContextWriterTest.java
index 4f1c6860..6255344a 100644
--- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/AwsProxySecurityContextWriterTest.java
+++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/AwsProxySecurityContextWriterTest.java
@@ -1,6 +1,7 @@
-package com.amazonaws.serverless.proxy.internal;
+package com.amazonaws.serverless.proxy;
-import com.amazonaws.serverless.proxy.internal.model.AwsProxyRequest;
+import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
+import com.amazonaws.serverless.proxy.AwsProxySecurityContextWriter;
import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
import com.amazonaws.services.lambda.runtime.Context;
import org.junit.Before;
diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/RequestReaderTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/RequestReaderTest.java
similarity index 95%
rename from aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/RequestReaderTest.java
rename to aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/RequestReaderTest.java
index 6bdc5d41..fb0d25f4 100644
--- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/RequestReaderTest.java
+++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/RequestReaderTest.java
@@ -1,7 +1,7 @@
-package com.amazonaws.serverless.proxy.internal;
+package com.amazonaws.serverless.proxy;
-import com.amazonaws.serverless.proxy.internal.model.ContainerConfig;
+import com.amazonaws.serverless.proxy.model.ContainerConfig;
import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletRequestReader;
import org.junit.Test;
diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/AwsProxyExceptionHandlerTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/AwsProxyExceptionHandlerTest.java
index ff503ff9..392fda32 100644
--- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/AwsProxyExceptionHandlerTest.java
+++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/AwsProxyExceptionHandlerTest.java
@@ -3,8 +3,8 @@
import com.amazonaws.serverless.exceptions.InvalidRequestEventException;
import com.amazonaws.serverless.exceptions.InvalidResponseObjectException;
-import com.amazonaws.serverless.proxy.internal.model.AwsProxyResponse;
-import com.amazonaws.serverless.proxy.internal.model.ErrorModel;
+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;
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 255f2a0d..da9f1168 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,6 +1,6 @@
package com.amazonaws.serverless.proxy.internal.jaxrs;
-import com.amazonaws.serverless.proxy.internal.model.AwsProxyRequest;
+import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
import org.junit.Test;
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 41c49dfc..e10526f7 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,6 +1,6 @@
package com.amazonaws.serverless.proxy.internal.servlet;
-import com.amazonaws.serverless.proxy.internal.model.AwsProxyRequest;
+import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -11,7 +11,6 @@
import static org.junit.Assert.*;
-import java.util.AbstractMap;
import java.util.List;
import java.util.Map;
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 62f3fe97..f49927d4 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,13 +1,11 @@
package com.amazonaws.serverless.proxy.internal.servlet;
-import com.amazonaws.serverless.proxy.internal.model.AwsProxyRequest;
+import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
-import org.apache.http.client.entity.EntityBuilder;
-import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.junit.Test;
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 fa883856..456e522d 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
@@ -2,8 +2,8 @@
import com.amazonaws.serverless.exceptions.InvalidRequestEventException;
-import com.amazonaws.serverless.proxy.internal.model.AwsProxyRequest;
-import com.amazonaws.serverless.proxy.internal.model.ContainerConfig;
+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.Context;
import org.junit.Test;
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 c7884a6e..862a5245 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,21 +1,15 @@
package com.amazonaws.serverless.proxy.internal.servlet;
-import com.amazonaws.serverless.proxy.internal.model.AwsProxyRequest;
+import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
-import org.apache.commons.io.IOUtils;
-import org.apache.http.HttpEntity;
-import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.junit.Test;
-import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
-import java.io.IOException;
import java.util.Collections;
-import java.util.Enumeration;
import java.util.List;
import static org.junit.Assert.*;
diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/model/ApiGatewayAuthorizerContextTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/ApiGatewayAuthorizerContextTest.java
similarity index 99%
rename from aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/model/ApiGatewayAuthorizerContextTest.java
rename to aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/ApiGatewayAuthorizerContextTest.java
index 75b80d81..87ab0ed7 100644
--- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/model/ApiGatewayAuthorizerContextTest.java
+++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/ApiGatewayAuthorizerContextTest.java
@@ -1,4 +1,4 @@
-package com.amazonaws.serverless.proxy.internal.model;
+package com.amazonaws.serverless.proxy.model;
import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/model/CognitoAuthorizerClaimsTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/CognitoAuthorizerClaimsTest.java
similarity index 99%
rename from aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/model/CognitoAuthorizerClaimsTest.java
rename to aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/CognitoAuthorizerClaimsTest.java
index 980ee188..1805edf3 100644
--- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/model/CognitoAuthorizerClaimsTest.java
+++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/model/CognitoAuthorizerClaimsTest.java
@@ -1,4 +1,4 @@
-package com.amazonaws.serverless.proxy.internal.model;
+package com.amazonaws.serverless.proxy.model;
import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
diff --git a/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyRequestReader.java b/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyRequestReader.java
index 143236af..e90902b6 100644
--- a/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyRequestReader.java
+++ b/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyRequestReader.java
@@ -14,9 +14,9 @@
import com.amazonaws.serverless.exceptions.InvalidRequestEventException;
-import com.amazonaws.serverless.proxy.internal.RequestReader;
-import com.amazonaws.serverless.proxy.internal.model.AwsProxyRequest;
-import com.amazonaws.serverless.proxy.internal.model.ContainerConfig;
+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 org.glassfish.jersey.internal.MapPropertiesDelegate;
import org.glassfish.jersey.internal.PropertiesDelegate;
@@ -63,7 +63,7 @@ public class JerseyAwsProxyRequestReader extends RequestReaderRequestReader object. This object reads an incoming AwsProxyRequest
- * event and transform it into a Jersey ContainerRequest object. The object sets three custom properties in the
- * request's PropertiesDelegate object: The API Gateway request context, the Map of stage variables, and the
- * Lambda context object.
- *
- * The useStageAsBasePath configuration variable lets you set whether the stage name should be included in the
- * request path passed to the Jersey application handler.
- */
-public class JerseyAwsProxyRequestReader extends RequestReaderContainerRequest object.
- *
- * @param request The incoming request object
- * @param securityContext A jax-rs SecurityContext object (@see com.amazonaws.serverless.proxy.SecurityContextWriter)
- * @param lambdaContext The AWS Lambda context for the request
- * @param config The container config object, this is passed in by the LambdaContainerHandler
- * @return A populated ContainerRequest object
- * @throws InvalidRequestEventException When the method fails to parse the incoming request
- */
- @Override
- public ContainerRequest readRequest(AwsProxyRequest request, SecurityContext securityContext, Context lambdaContext, ContainerConfig config)
- throws InvalidRequestEventException {
- currentRequest = request;
- currentLambdaContext = lambdaContext;
-
- request.setPath(stripBasePath(request.getPath(), config));
-
- URI basePathUri;
- URI requestPathUri;
- String basePath = "/";
-
- try {
- basePathUri = new URI(basePath);
- } catch (URISyntaxException e) {
- log.error("Could not read base path URI", e);
- throw new InvalidRequestEventException("Error while generating base path URI: " + basePath, e);
- }
-
-
- UriBuilder uriBuilder = UriBuilder.fromPath(request.getPath());
-
- if (request.getQueryStringParameters() != null) {
- for (String paramKey : request.getQueryStringParameters().keySet()) {
- uriBuilder = uriBuilder.queryParam(paramKey, request.getQueryStringParameters().get(paramKey));
- }
- }
-
- requestPathUri = uriBuilder.build();
-
- PropertiesDelegate apiGatewayProperties = new MapPropertiesDelegate();
- apiGatewayProperties.setProperty(API_GATEWAY_CONTEXT_PROPERTY, request.getRequestContext());
- apiGatewayProperties.setProperty(API_GATEWAY_STAGE_VARS_PROPERTY, request.getStageVariables());
- apiGatewayProperties.setProperty(LAMBDA_CONTEXT_PROPERTY, lambdaContext);
-
- ContainerRequest requestContext = new ContainerRequest(basePathUri, requestPathUri, request.getHttpMethod(), securityContext, apiGatewayProperties);
-
- if (request.getBody() != null) {
- if (request.isBase64Encoded()) {
- requestContext.setEntityStream(new ByteArrayInputStream(Base64.getDecoder().decode(request.getBody())));
- } else {
- requestContext.setEntityStream(new ByteArrayInputStream(request.getBody().getBytes()));
- }
- }
-
- if (request.getHeaders() != null) {
- for (final String headerName : request.getHeaders().keySet()) {
- requestContext.headers(headerName, request.getHeaders().get(headerName));
- }
- }
-
- return requestContext;
- }
-
- //-------------------------------------------------------------
- // Methods - Protected
- //-------------------------------------------------------------
-
- @Override
- protected Class extends AwsProxyRequest> getRequestClass() {
- return AwsProxyRequest.class;
- }
-
-
- //-------------------------------------------------------------
- // Methods - Package
- //-------------------------------------------------------------
-
- public static AwsProxyRequest getCurrentRequest() {
- return currentRequest;
- }
-
-
- public static Context getCurrentLambdaContext() {
- return currentLambdaContext;
- }
-}
diff --git a/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyResponseWriter.java b/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyResponseWriter.java
deleted file mode 100644
index cbf9f18a..00000000
--- a/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyAwsProxyResponseWriter.java
+++ /dev/null
@@ -1,73 +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.jersey;
-
-
-import com.amazonaws.serverless.exceptions.InvalidResponseObjectException;
-import com.amazonaws.serverless.proxy.ResponseWriter;
-import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
-import com.amazonaws.services.lambda.runtime.Context;
-
-import java.util.Base64;
-
-
-/**
- * Transforms the data from a JerseyResponseWriter object into a valid AwsProxyResponse object.
- *
- * @see com.amazonaws.serverless.proxy.jersey.JerseyResponseWriter
- * @see AwsProxyResponse
- */
-public class JerseyAwsProxyResponseWriter extends ResponseWriterApplicationHandler
+ * object is re-initialized with the Application object initially set in the LambdaContainer.getInstance()
+ * call.
+ */
+ @Override
+ public void reload() {
+ jersey.onShutdown(this);
+
+ jersey = new ApplicationHandler(app);
+
+ jersey.onReload(this);
+ jersey.onStartup(this);
+ }
+
+
+ /**
+ * Restarts the application handler and configures a different Application object. The new application
+ * resets the one currently configured in the container.
+ * @param resourceConfig An initialized Application
+ */
+ @Override
+ public void reload(ResourceConfig resourceConfig) {
+ jersey.onShutdown(this);
+
+ app = resourceConfig;
+ jersey = new ApplicationHandler(resourceConfig);
+
+ jersey.onReload(this);
+ jersey.onStartup(this);
+ }
+}
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 297a5962..2eaaeec9 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
@@ -20,6 +20,11 @@
import com.amazonaws.serverless.proxy.RequestReader;
import com.amazonaws.serverless.proxy.ResponseWriter;
import com.amazonaws.serverless.proxy.SecurityContextWriter;
+import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletResponse;
+import com.amazonaws.serverless.proxy.internal.servlet.AwsLambdaServletContainerHandler;
+import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletRequest;
+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;
@@ -29,8 +34,11 @@
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.spi.Container;
+import javax.servlet.DispatcherType;
+import javax.servlet.FilterRegistration;
import javax.ws.rs.core.Application;
+import java.util.EnumSet;
import java.util.concurrent.CountDownLatch;
@@ -58,8 +66,7 @@
* @param JerseyLambdaContainerHandler object
*/
public static JerseyLambdaContainerHandlerApplicationHandler
- *
- * @return The Jersey's ResourceConfig object currently running in the container
- */
- public ResourceConfig getConfiguration() {
- return applicationHandler.getConfiguration();
- }
-
-
- /**
- * The instantiated ApplicationHandler object used by this container
- *
- * @return Jersey's ApplicationHander object
- */
- public ApplicationHandler getApplicationHandler() {
- return applicationHandler;
- }
-
-
- /**
- * Shuts down and restarts the application handler in the current container. The ApplicationHandler
- * object is re-initialized with the Application object initially set in the LambdaContainer.getInstance()
- * call.
- */
- public void reload() {
- applicationHandler.onShutdown(this);
-
- this.applicationHandler = new ApplicationHandler(jaxRsApplication);
-
- applicationHandler.onReload(this);
- applicationHandler.onStartup(this);
+ @Override
+ protected AwsHttpServletResponse getContainerResponse(AwsProxyHttpServletRequest request, CountDownLatch latch) {
+ return new AwsHttpServletResponse(request, latch);
}
+ @Override
+ protected void handleRequest(AwsProxyHttpServletRequest httpServletRequest, AwsHttpServletResponse httpServletResponse, Context lambdaContext)
+ throws Exception {
- /**
- * Restarts the application handler and configures a different Application object. The new application
- * resets the one currently configured in the container.
- * @param resourceConfig An initialized Application
- */
- public void reload(ResourceConfig resourceConfig) {
- applicationHandler.onShutdown(this);
-
- this.jaxRsApplication = resourceConfig;
- this.applicationHandler = new ApplicationHandler(resourceConfig);
-
- applicationHandler.onReload(this);
- applicationHandler.onStartup(this);
- }
-
+ // this method of the AwsLambdaServletContainerHandler sets the request context
+ super.handleRequest(httpServletRequest, httpServletResponse, lambdaContext);
- //-------------------------------------------------------------
- // Methods - Implementation
- //-------------------------------------------------------------
+ if (!initialized) {
+ // call the onStartup event if set to give developers a chance to set filters in the context
+ if (startupHandler != null) {
+ startupHandler.onStartup(getServletContext());
+ }
- @Override
- protected JerseyResponseWriter getContainerResponse(ContainerRequest request, CountDownLatch latch) {
- return new JerseyResponseWriter(latch);
- }
+ // manually add the spark filter to the chain. This should the last one and match all uris
+ FilterRegistration.Dynamic jerseyFilterReg = getServletContext().addFilter("JerseyFilter", jerseyFilter);
+ jerseyFilterReg.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
+ initialized = true;
+ }
- @Override
- protected void handleRequest(ContainerRequest containerRequest, JerseyResponseWriter jerseyResponseWriter, Context lambdaContext) {
- containerRequest.setWriter(jerseyResponseWriter);
+ httpServletRequest.setServletContext(getServletContext());
- applicationHandler.handle(containerRequest);
+ doFilter(httpServletRequest, httpServletResponse, null);
}
}
diff --git a/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyResponseWriter.java b/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyServletResponseWriter.java
similarity index 59%
rename from aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyResponseWriter.java
rename to aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyServletResponseWriter.java
index 89a038b1..93ecc900 100644
--- a/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyResponseWriter.java
+++ b/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyServletResponseWriter.java
@@ -13,17 +13,18 @@
package com.amazonaws.serverless.proxy.jersey;
-import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
-
import org.glassfish.jersey.server.ContainerException;
import org.glassfish.jersey.server.ContainerResponse;
import org.glassfish.jersey.server.spi.ContainerResponseWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-import javax.ws.rs.core.HttpHeaders;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.InternalServerErrorException;
-import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.io.OutputStream;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
@@ -35,18 +36,16 @@
* AwsProxyResponse object. The response object is passed in the constructor alongside an ExceptionHandler
* instance.
*/
-class JerseyResponseWriter
+class JerseyServletResponseWriter
implements ContainerResponseWriter {
//-------------------------------------------------------------
// Variables - Private
//-------------------------------------------------------------
- private CountDownLatch responseMutex;
- private MapFactory object for HttpServletRequest objects. This can be used
@@ -48,7 +45,7 @@
public class AwsProxyServletRequestFactory
implements FactoryFactory object for HttpServletRequest objects. This can be used
+ * by Jersey to generate a Servlet request given an AwsProxyRequest event.
+ *
+ *
+ *
+ * ResourceConfig app = new ResourceConfig().packages("my.app.package")
+ * .register(new AbstractBinder() {
+ * {@literal @}Override
+ * protected void configure() {
+ * bindFactory(AwsProxyServletRequestFactory.class)
+ * .to(HttpServletRequest.class)
+ * .in(RequestScoped.class);
+ * }
+ * });
+ *
+ *
+ */
+public class AwsProxyServletResponseFactory
+ implements FactoryLambdaContainerHandler object that supports the Spark framework: http://sparkjava.com/
- *
+ * * Because of the way this container is implemented, using reflection to change accessibility of methods in the Spark * framework and inserting itself as the default embedded container, it is important that you initialize the Handler * before declaring your spark routes. - * + *
* This implementation uses the default AwsProxyHttpServletRequest and Response implementations.
- *
+ *
*
* {@code
* // always initialize the handler first
@@ -64,10 +70,12 @@
* });
* }
*
+ *
* @param RequestReader implementation passed to the constructor
* @param ResponseWriter implementation in the constructor
*/
-public class SparkLambdaContainerHandlerAwsProxyRequest
* and AwsProxyResponse objects.
*
* @return a new instance of SparkLambdaContainerHandler
+ *
* @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
+ * This could be caused by the introspection used to insert the library as the default embedded container
*/
public static SparkLambdaContainerHandlerFactory object for HttpServletRequest objects. This can be used
- * by Jersey to generate a Servlet request given an AwsProxyRequest event.
+ * Implementation of Jersey's Factory object for HttpServletResponse objects. This can be used
+ * to write data directly to the servlet response for the method, without using Jersey's ContainerResponse
*
* *@@ -36,8 +36,8 @@ * .register(new AbstractBinder() { * {@literal @}Override * protected void configure() { - * bindFactory(AwsProxyServletRequestFactory.class) - * .to(HttpServletRequest.class) + * bindFactory(AwsProxyServletResponseFactory.class) + * .to(HttpServletResponse.class) * .in(RequestScoped.class); * } * }); From 9f9f139de5993a1f261d2be586aef0c64cf2fb00 Mon Sep 17 00:00:00 2001 From: sapessito intercept the ServletContext as the Spring application is 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 4080e1c7..1c719e02 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 @@ -1,6 +1,8 @@ package com.amazonaws.serverless.proxy.internal.servlet; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + import javax.servlet.DispatcherType; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; @@ -48,6 +50,7 @@ public AwsProxyRequestDispatcher(final String path, final AwsLambdaServletContai @Override @SuppressWarnings("unchecked") + @SuppressFBWarnings("BC_UNCONFIRMED_CAST") public void forward(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { if (!(servletRequest instanceof AwsProxyHttpServletRequest)) { @@ -67,6 +70,7 @@ public void forward(ServletRequest servletRequest, ServletResponse servletRespon @Override @SuppressWarnings("unchecked") + @SuppressFBWarnings("BC_UNCONFIRMED_CAST") public void include(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { if (!(servletRequest instanceof AwsProxyHttpServletRequest)) { diff --git a/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyServletResponseWriter.java b/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyServletResponseWriter.java index 8e15112e..a7df1f18 100644 --- a/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyServletResponseWriter.java +++ b/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyServletResponseWriter.java @@ -60,6 +60,7 @@ class JerseyServletResponseWriter * @param resp The current ServletResponse from the container */ public JerseyServletResponseWriter(ServletResponse resp, CountDownLatch latch) { + assert resp instanceof HttpServletResponse; servletResponse = (HttpServletResponse)resp; jerseyLatch = latch; } From 28242b7e42d8e1e0f5bd0d585d4f38dec58e6541 Mon Sep 17 00:00:00 2001 From: sapessiDate: Mon, 22 Jan 2018 14:21:13 -0800 Subject: [PATCH 0081/1214] Cleanup for README preparing for 0.9 release. Documentation is now in the wiki --- README.md | 212 ++++-------------------------------------------------- 1 file changed, 14 insertions(+), 198 deletions(-) diff --git a/README.md b/README.md index fedd49f7..31fbb296 100644 --- a/README.md +++ b/README.md @@ -1,205 +1,20 @@ # Serverless Java container [](https://travis-ci.org/awslabs/aws-serverless-java-container) [](https://gitter.im/awslabs/aws-serverless-java-container) -The `aws-serverless-java-container` is collection of interfaces and their implementations that let you run Java application written with frameworks such as [Jersey](https://jersey.java.net/) or [Spark](http://sparkjava.com/) in [AWS Lambda](https://aws.amazon.com/lambda/). +The `aws-serverless-java-container` makes it easy to run Java applications written wih frameworks such as [Spring](https://spring.io/), [Spring Boot](https://projects.spring.io/spring-boot/), [Jersey](https://jersey.java.net/), or [Spark](http://sparkjava.com/) in [AWS Lambda](https://aws.amazon.com/lambda/). -The library contains a core artifact called `aws-serverless-java-container-core` that defines the interfaces and base classes required as well as default implementation of the Java servlet `HttpServletRequest` and `HttpServletResponse`. -The library also includes two initial implementations of the interfaces to support Jersey apps (`aws-serverless-java-container-jersey`) and Spark (`aws-serverless-java-container-spark`). +Serverless Java Container natively supports API Gateway's proxy integration models for requests and responses, you can create and inject custom models for methods that use custom mappings. -To include the library in your Maven project, add the desired implementation to your `pom.xml` file, for example: +Follow the quick started guides in [our wiki](wiki) to integrate Serverless Java Container with your project: +* [Spring quick start](wiki/Quick-start---Spring) +* [Spring Boot quick start](wiki/Quick-start---Spring-Boot) +* [Jersey quick start](wiki/Quick-start---Jersey) +* [Spark quick start](wiki/Quick-start---Spark) -``` - - -``` - -## Integrating with Lambda -The simplest way to run your application serverlessly is to configure [API Gateway](https://aws.amazon.com/api-gateway/) to use the -[`AWS_PROXY`](http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-set-up-simple-proxy.html#api-gateway-set-up-lambda-proxy-integration-on-proxy-resource) integration type and -configure your desired `LambdaContainerHandler` implementation to use `AwsProxyRequest`/`AwsProxyResponse` readers and writers. Both Spark and Jersey implementations provide static helper methods that -pre-configure this for you. - -When using a Cognito User Pool authorizer, use the Lambda `RequestStreamHandler` instead of the POJO-based `RequestHandler` handler. An example of this is included at the bottom of this file. The POJO handler does not support Jackson annotations required for the `CognitoAuthorizerClaims` class. - -### Jersey support -The library expects to receive a valid [JAX-RS](https://jax-rs-spec.java.net) application object. For the Jersey implementation this is the `ResourceConfig` object. - -```java -public class LambdaHandler implements RequestHandlercom.amazonaws.serverless -aws-serverless-java-container-jersey -0.8 -{ - private ResourceConfig jerseyApplication = new ResourceConfig().packages("my.jersey.app.package"); - private JerseyLambdaContainerHandler handler - = JerseyLambdaContainerHandler.getAwsProxyHandler(jerseyApplication); - - public AwsProxyResponse handleRequest(AwsProxyRequest awsProxyRequest, Context context) { - return handler.proxy(awsProxyRequest, context); - } -} -``` - -### Spring support -The library supports Spring applications that are configured using annotations (in code) rather than in an XML file. The simplest possible configuration uses the `@ComponentScan` annotation to load all controller classes from a package. For example, our unit test application has the following configuration class. - -```java -@Configuration -@ComponentScan("com.amazonaws.serverless.proxy.spring.echoapp") -public class EchoSpringAppConfig { -} -``` - -Once you have declared a configuration class, you can initialize the library with the class name: -```java -public class LambdaHandler implements RequestHandler { - SpringLambdaContainerHandler handler = - SpringLambdaContainerHandler.getAwsProxyHandler(EchoSpringAppConfig.class); - - public AwsProxyResponse handleRequest(AwsProxyRequest awsProxyRequest, Context context) { - return handler.proxy(awsProxyRequest, context); - } -} -``` - -#### Spring Profiles -You can enable Spring Profiles (as defined with the `@Profile` annotation) by using the `SpringLambdaContainerHandler.activateSpringProfiles(String...)` method - common drivers of this might be the AWS Lambda stage that you're deployed under, or stage variables. See [@Profile documentation](http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Profile.html) for details. - -#### Spring Boot -You can also use this framework to start Spring Boot applications inside Lambda. The framework does not recognize classes annotated with `@SpringBootApplication` automatically. However, you can wrap the Spring Boot application class in a regular `ConfigurableWebApplicationContext` object. In your handler class, instead of initializing the `SpringLambdaContainerHandler` with the Spring Boot application class, initialize another context and set the Spring Boot app as a parent: - -```java -SpringApplication springBootApplication = new SpringApplication(SpringBootApplication.class); -springBootApplication.setWebEnvironment(false); -springBootApplication.setBannerMode(Banner.Mode.OFF); - -// create a new empty context and set the spring boot application as a parent of it -ConfigurableWebApplicationContext wrappingContext = new AnnotationConfigWebApplicationContext(); -wrappingContext.setParent(springBootApplication.run()); - -// now we can initialize the framework with the wrapping context -SpringLambdaContainerHandler handler = - SpringLambdaContainerHandler.getAwsProxyHandler(wrappingContext); -``` - -When using Spring Boot, make sure to configure the shade plugin in your pom file to exclude the embedded container and all unnecessary libraries to reduce the size of your built jar. - -### Spark support -The library also supports applications written with the [Spark framework](http://sparkjava.com/). When using the library with Spark, it's important to initialize the `SparkLambdaContainerHandler` before defining routes. - -```java -public class LambdaHandler implements RequestHandler { - private SparkLambdaContainerHandler handler = - SparkLambdaContainerHandler.getAwsProxyHandler(); - private boolean initialized = false; - - public AwsProxyResponse handleRequest(AwsProxyRequest awsProxyRequest, Context context) { - if (!initialized) { - defineRoutes(); - // it's important to call the awaitInitialization method not to run into race - // conditions as routes are loaded asynchronously - Spark.awaitInitialization(); - initialized = true; - } - return handler.proxy(awsProxyRequest, context); - } - - private void defineRoutes() { - get("/hello", (req, res) -> "Hello World"); - } -} -``` - -If you configure an [`initExceptionHandler` method](http://sparkjava.com/documentation#stopping-the-server), make sure that you call `System.exit` at the end of the method. This framework keeps a `CountDownLatch` on the request -and unless you forcefully exit from the thread, the Lambda function will hang waiting for a latch that is never released. - -```java -initExceptionHandler((e) -> { - LOG.error("ignite failed", e); - System.exit(100); -}); -``` - -# Security context -The `aws-serverless-java-container-core` contains a default implementation of the `SecurityContextWriter` that supports API Gateway's proxy integration. The generated security context uses the API Gateway `$context` object to establish the request security context. The context looks for the following values in order and returns the first matched type: - -1. Cognito My User Pools -2. Custom authorizers -3. IAM auth. - -The String values for these are exposed as static variables in the `AwsProxySecurityContext` object. - -1. `AUTH_SCHEME_COGNITO_POOL` -2. `AUTH_SCHEME_CUSTOM` -3. `AUTH_SCHEME_IAM` - -# Supporting other event types -The `RequestReader` and `ResponseWriter` interfaces in the core package can be used to support event types and generate different responses. For example, ff you have configured mapping templates in -API Gateway to create a custom event body or response you can create your own implementation of the `RequestReader` and `ResponseWriter` to handle these. - -The `LambdaContainerHandler` also requires a `SecurityContextWriter` and an `ExceptionHandler`. You can also create custom implementations of these interfaces. - -The `RequestReader`, `ResponseWriter`, `SecurityContextWriter`, and `ExceptionHandler` objects are passed to the constructor of the `LambdaContainerHandler` implementation: - -```java -JerseyLambdaContainerHandler handler = - new JerseyLambdaContainerHandler<>(new MyCustomRequestReader(), - new MyCustomResponseWriter(), - new MyCustomSecurityContextWriter(), - new MyCustomExceptionHandler(), - jaxRsApplication); -``` - -# Jersey Servlet injection -The `aws-serverless-java-container-jersey` includes Jersey factory classes to produce `HttpServletRequest` and `ServletContext` objects for your methods. First, you will need to register the factory with your Jersey application. - -```java -ResourceConfig app = new ResourceConfig() - .packages("com.amazonaws.serverless.proxy.test.jersey") - .register(new AbstractBinder() { - @Override - protected void configure() { - bindFactory(AwsProxyServletRequestFactory.class) - .to(HttpServletRequest.class) - .in(RequestScoped.class); - bindFactory(AwsProxyServletContextFactory.class) - .to(ServletContext.class) - .in(RequestScoped.class); - } - }); -``` - -Once the factory is registered, you can receive `HttpServletRequest` and `ServletContext` objects in your methods using the `@Context` annotation. - -```java -@Path("/my-servlet") @GET -public String echoServletHeaders(@Context HttpServletRequest context) { - Enumeration headerNames = context.getHeaderNames(); - while (headerNames.hasMoreElements()) { - String headerName = headerNames.nextElement(); - } - return "servlet"; -} -``` - -## Servlet Filters -You can register [`Filter`](https://docs.oracle.com/javaee/7/api/javax/servlet/Filter.html) implementations by implementing a `StartupsHandler` as defined in the `AwsLambdaServletContainerHandler` class. The `onStartup` methods receives a reference to the current `ServletContext`. - -```java -handler.onStartup(c -> { - FilterRegistration.Dynamic registration = c.addFilter("CustomHeaderFilter", CustomHeaderFilter.class); - // update the registration to map to a path - registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*"); - // servlet name mappings are disabled and will throw an exception -}); -``` - -# Using the Lambda Stream handler -By default, Lambda does not use Jackson annotations when marshalling and unmarhsalling JSON. This can cause issues when receiving requests that include the claims object from a Cognito User Pool authorizer. To support these type of requests, use Lambda's `RequestStreamHandler` interface instead of the POJO-based `RequestHandler`. This allows you to use a custom version of Jackson with support for annotations. - -This library uses Jackson annotations in the `com.amazonaws.serverless.proxy.model.CognitoAuthorizerClaims` object. The example below shows how to do this with a `SpringLambdaContainerHandler`, you can use the same methodology with all of the other implementations. +Below is the most basic AWS Lambda handler example that launches a Spring application. You can also take a look at the [samples](tree/master/samples) in this repository, our main wiki page includes a [step-by-step guide](wiki#deploying-the-sample-applications) on how to deploy the various sample applications using Maven and [SAM](https://github.com/awslabs/serverless-application-model). ```java public class StreamLambdaHandler implements RequestStreamHandler { private SpringLambdaContainerHandler handler; - private static ObjectMapper mapper = new ObjectMapper(); + private Logger log = LoggerFactory.getLogger(StreamLambdaHandler.class); @Override public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) @@ -208,18 +23,19 @@ public class StreamLambdaHandler implements RequestStreamHandler { try { handler = SpringLambdaContainerHandler.getAwsProxyHandler(PetStoreSpringAppConfig.class); } catch (ContainerInitializationException e) { - e.printStackTrace(); + log.error("Cannot initialize Spring container", e); outputStream.close(); + throw new RuntimeException(e); } } - AwsProxyRequest request = mapper.readValue(inputStream, AwsProxyRequest.class); + AwsProxyRequest request = LambdaContainerHandler.getObjectMapper().readValue(inputStream, AwsProxyRequest.class); AwsProxyResponse resp = handler.proxy(request, context); - mapper.writeValue(outputStream, resp); + LambdaContainerHandler.getObjectMapper().writeValue(outputStream, resp); // just in case it wasn't closed by the mapper outputStream.close(); } } -``` +``` \ No newline at end of file From 6893a4ccd88b2f8587d540f694fb42866b2c283b Mon Sep 17 00:00:00 2001 From: sapessi Date: Mon, 22 Jan 2018 14:23:20 -0800 Subject: [PATCH 0082/1214] Fixed README to use absolute links --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 31fbb296..94cb648c 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,13 @@ The `aws-serverless-java-container` makes it easy to run Java applications writt Serverless Java Container natively supports API Gateway's proxy integration models for requests and responses, you can create and inject custom models for methods that use custom mappings. -Follow the quick started guides in [our wiki](wiki) to integrate Serverless Java Container with your project: -* [Spring quick start](wiki/Quick-start---Spring) -* [Spring Boot quick start](wiki/Quick-start---Spring-Boot) -* [Jersey quick start](wiki/Quick-start---Jersey) -* [Spark quick start](wiki/Quick-start---Spark) +Follow the quick started guides in [our wiki](https://github.com/awslabs/aws-serverless-java-container/wiki) to integrate Serverless Java Container with your project: +* [Spring quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Spring) +* [Spring Boot quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Spring-Boot) +* [Jersey quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Jersey) +* [Spark quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Spark) -Below is the most basic AWS Lambda handler example that launches a Spring application. You can also take a look at the [samples](tree/master/samples) in this repository, our main wiki page includes a [step-by-step guide](wiki#deploying-the-sample-applications) on how to deploy the various sample applications using Maven and [SAM](https://github.com/awslabs/serverless-application-model). +Below is the most basic AWS Lambda handler example that launches a Spring application. You can also take a look at the [samples](https://github.com/awslabs/aws-serverless-java-container/tree/master/samples) in this repository, our main wiki page includes a [step-by-step guide](https://github.com/awslabs/aws-serverless-java-container/wiki#deploying-the-sample-applications) on how to deploy the various sample applications using Maven and [SAM](https://github.com/awslabs/serverless-application-model). ```java public class StreamLambdaHandler implements RequestStreamHandler { From 83e65be488a8c05c6b5f32e2c72469a2d3cc1160 Mon Sep 17 00:00:00 2001 From: sapessi Date: Mon, 22 Jan 2018 15:03:55 -0800 Subject: [PATCH 0083/1214] [maven-release-plugin] prepare release aws-serverless-java-container-0.9 --- aws-serverless-java-container-core/pom.xml | 4 ++-- aws-serverless-java-container-jersey/pom.xml | 6 +++--- aws-serverless-java-container-spark/pom.xml | 6 +++--- aws-serverless-java-container-spring/pom.xml | 6 +++--- pom.xml | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/aws-serverless-java-container-core/pom.xml b/aws-serverless-java-container-core/pom.xml index 9794788d..ac507b06 100644 --- a/aws-serverless-java-container-core/pom.xml +++ b/aws-serverless-java-container-core/pom.xml @@ -6,12 +6,12 @@ AWS Serverless Java container support - Core Allows Java applications written for a servlet container to run in AWS Lambda https://aws.amazon.com/lambda -0.9-SNAPSHOT +0.9 com.amazonaws.serverless aws-serverless-java-container -0.9-SNAPSHOT +0.9 diff --git a/aws-serverless-java-container-jersey/pom.xml b/aws-serverless-java-container-jersey/pom.xml index 920c8d40..73ae219b 100644 --- a/aws-serverless-java-container-jersey/pom.xml +++ b/aws-serverless-java-container-jersey/pom.xml @@ -6,12 +6,12 @@ AWS Serverless Java container support - Jersey implementation Allows Java applications written for Jersey to run in AWS Lambda https://aws.amazon.com/lambda -0.9-SNAPSHOT +0.9 com.amazonaws.serverless aws-serverless-java-container -0.9-SNAPSHOT +0.9 @@ -23,7 +23,7 @@ com.amazonaws.serverless aws-serverless-java-container-core -0.9-SNAPSHOT +0.9 com.fasterxml.jackson.core diff --git a/aws-serverless-java-container-spark/pom.xml b/aws-serverless-java-container-spark/pom.xml index bc1a1adf..d6b3b10c 100644 --- a/aws-serverless-java-container-spark/pom.xml +++ b/aws-serverless-java-container-spark/pom.xml @@ -6,12 +6,12 @@AWS Serverless Java container support - Spark implementation Allows Java applications written for Spark to run in AWS Lambda https://aws.amazon.com/lambda -0.9-SNAPSHOT +0.9 com.amazonaws.serverless aws-serverless-java-container -0.9-SNAPSHOT +0.9 @@ -23,7 +23,7 @@ diff --git a/aws-serverless-java-container-spring/pom.xml b/aws-serverless-java-container-spring/pom.xml index b12554d3..6db31810 100644 --- a/aws-serverless-java-container-spring/pom.xml +++ b/aws-serverless-java-container-spring/pom.xml @@ -6,12 +6,12 @@ com.amazonaws.serverless aws-serverless-java-container-core -0.9-SNAPSHOT +0.9 AWS Serverless Java container support - Spring implementation Allows Java applications written for the Spring framework to run in AWS Lambda https://aws.amazon.com/lambda -0.9-SNAPSHOT +0.9 com.amazonaws.serverless aws-serverless-java-container -0.9-SNAPSHOT +0.9 @@ -24,7 +24,7 @@ diff --git a/pom.xml b/pom.xml index d0456781..f5da7d1a 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.amazonaws.serverless aws-serverless-java-container-core -0.9-SNAPSHOT +0.9 com.amazonaws.serverless aws-serverless-java-container pom -0.9-SNAPSHOT +0.9 AWS Serverless Java container From ccab5b6f675d08836ff0e9d6d555b86880bc64df Mon Sep 17 00:00:00 2001 From: sapessiDate: Mon, 22 Jan 2018 15:03:56 -0800 Subject: [PATCH 0084/1214] [maven-release-plugin] prepare for next development iteration --- aws-serverless-java-container-core/pom.xml | 4 ++-- aws-serverless-java-container-jersey/pom.xml | 6 +++--- aws-serverless-java-container-spark/pom.xml | 6 +++--- aws-serverless-java-container-spring/pom.xml | 6 +++--- pom.xml | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/aws-serverless-java-container-core/pom.xml b/aws-serverless-java-container-core/pom.xml index ac507b06..8cb3133f 100644 --- a/aws-serverless-java-container-core/pom.xml +++ b/aws-serverless-java-container-core/pom.xml @@ -6,12 +6,12 @@ AWS Serverless Java container support - Core Allows Java applications written for a servlet container to run in AWS Lambda https://aws.amazon.com/lambda -0.9 +1.0-SNAPSHOT com.amazonaws.serverless aws-serverless-java-container -0.9 +1.0-SNAPSHOT diff --git a/aws-serverless-java-container-jersey/pom.xml b/aws-serverless-java-container-jersey/pom.xml index 73ae219b..9e1532d8 100644 --- a/aws-serverless-java-container-jersey/pom.xml +++ b/aws-serverless-java-container-jersey/pom.xml @@ -6,12 +6,12 @@ AWS Serverless Java container support - Jersey implementation Allows Java applications written for Jersey to run in AWS Lambda https://aws.amazon.com/lambda -0.9 +1.0-SNAPSHOT com.amazonaws.serverless aws-serverless-java-container -0.9 +1.0-SNAPSHOT @@ -23,7 +23,7 @@ + com.amazonaws.serverless aws-serverless-java-container-core -0.9 +1.0-SNAPSHOT com.fasterxml.jackson.core diff --git a/aws-serverless-java-container-spark/pom.xml b/aws-serverless-java-container-spark/pom.xml index d6b3b10c..91738601 100644 --- a/aws-serverless-java-container-spark/pom.xml +++ b/aws-serverless-java-container-spark/pom.xml @@ -6,12 +6,12 @@AWS Serverless Java container support - Spark implementation Allows Java applications written for Spark to run in AWS Lambda https://aws.amazon.com/lambda -0.9 +1.0-SNAPSHOT com.amazonaws.serverless aws-serverless-java-container -0.9 +1.0-SNAPSHOT @@ -23,7 +23,7 @@ diff --git a/aws-serverless-java-container-spring/pom.xml b/aws-serverless-java-container-spring/pom.xml index 6db31810..a0ff669b 100644 --- a/aws-serverless-java-container-spring/pom.xml +++ b/aws-serverless-java-container-spring/pom.xml @@ -6,12 +6,12 @@ com.amazonaws.serverless aws-serverless-java-container-core -0.9 +1.0-SNAPSHOT AWS Serverless Java container support - Spring implementation Allows Java applications written for the Spring framework to run in AWS Lambda https://aws.amazon.com/lambda -0.9 +1.0-SNAPSHOT com.amazonaws.serverless aws-serverless-java-container -0.9 +1.0-SNAPSHOT @@ -24,7 +24,7 @@ diff --git a/pom.xml b/pom.xml index f5da7d1a..49775a8d 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.amazonaws.serverless aws-serverless-java-container-core -0.9 +1.0-SNAPSHOT com.amazonaws.serverless aws-serverless-java-container pom -0.9 +1.0-SNAPSHOT AWS Serverless Java container From c46b68b6186f90f1cab063f8277f923b3a1e4d0a Mon Sep 17 00:00:00 2001 From: RosseynDate: Tue, 23 Jan 2018 17:06:19 -0800 Subject: [PATCH 0085/1214] Quick typo fixes --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 94cb648c..36726d72 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # Serverless Java container [](https://travis-ci.org/awslabs/aws-serverless-java-container) [](https://gitter.im/awslabs/aws-serverless-java-container) -The `aws-serverless-java-container` makes it easy to run Java applications written wih frameworks such as [Spring](https://spring.io/), [Spring Boot](https://projects.spring.io/spring-boot/), [Jersey](https://jersey.java.net/), or [Spark](http://sparkjava.com/) in [AWS Lambda](https://aws.amazon.com/lambda/). +The `aws-serverless-java-container` makes it easy to run Java applications written with frameworks such as [Spring](https://spring.io/), [Spring Boot](https://projects.spring.io/spring-boot/), [Jersey](https://jersey.java.net/), or [Spark](http://sparkjava.com/) in [AWS Lambda](https://aws.amazon.com/lambda/). Serverless Java Container natively supports API Gateway's proxy integration models for requests and responses, you can create and inject custom models for methods that use custom mappings. -Follow the quick started guides in [our wiki](https://github.com/awslabs/aws-serverless-java-container/wiki) to integrate Serverless Java Container with your project: +Follow the quick start guides in [our wiki](https://github.com/awslabs/aws-serverless-java-container/wiki) to integrate Serverless Java Container with your project: * [Spring quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Spring) * [Spring Boot quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Spring-Boot) * [Jersey quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Jersey) @@ -38,4 +38,4 @@ public class StreamLambdaHandler implements RequestStreamHandler { outputStream.close(); } } -``` \ No newline at end of file +``` From 5fbba8e19d90457f88bb295b79463643a89058fc Mon Sep 17 00:00:00 2001 From: sapessi Date: Wed, 24 Jan 2018 15:25:42 -0800 Subject: [PATCH 0086/1214] Removed print of Principal from sample method --- .../serverless/sample/springboot/controller/PetsController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/samples/springboot/pet-store/src/main/java/com/amazonaws/serverless/sample/springboot/controller/PetsController.java b/samples/springboot/pet-store/src/main/java/com/amazonaws/serverless/sample/springboot/controller/PetsController.java index cd879543..2680d111 100644 --- a/samples/springboot/pet-store/src/main/java/com/amazonaws/serverless/sample/springboot/controller/PetsController.java +++ b/samples/springboot/pet-store/src/main/java/com/amazonaws/serverless/sample/springboot/controller/PetsController.java @@ -45,7 +45,6 @@ public Pet createPet(@RequestBody Pet newPet) { @RequestMapping(path = "/pets", method = RequestMethod.GET) public Pet[] listPets(@RequestParam("limit") Optional limit, Principal principal) { - System.out.println(principal.getName()); int queryLimit = 10; if (limit.isPresent()) { queryLimit = limit.get(); From 68658d83a132dcdd6745e548ff5590a6f2a4c3b9 Mon Sep 17 00:00:00 2001 From: sapessi Date: Wed, 31 Jan 2018 07:11:47 -0800 Subject: [PATCH 0087/1214] Fixes to address Jersey routing issue reported in #112. This is a new bug introduced with the fixes for #84. Added unit tests --- .../proxy/jersey/JerseyHandlerFilter.java | 14 +---------- .../proxy/jersey/JerseyAwsProxyTest.java | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyHandlerFilter.java b/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyHandlerFilter.java index e36a044f..ee682dbf 100644 --- a/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyHandlerFilter.java +++ b/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/JerseyHandlerFilter.java @@ -97,22 +97,10 @@ public void destroy() { @SuppressFBWarnings({ "SERVLET_HEADER", "SERVLET_QUERY_STRING" }) private ContainerRequest servletRequestToContainerRequest(ServletRequest request) { Timer.start("JERSEY_SERVLET_REQUEST_TO_CONTAINER"); - URI basePathUri; URI requestPathUri; String basePath = "/"; HttpServletRequest servletRequest = (HttpServletRequest)request; - try { - if (servletRequest.getContextPath().equals("")) { - basePathUri = URI.create(basePath); - } else { - basePathUri = new URI(servletRequest.getContextPath()); - } - } catch (URISyntaxException e) { - log.error("Could not read base path URI", e); - basePathUri = URI.create(basePath); - } - UriBuilder uriBuilder = UriBuilder.fromPath(servletRequest.getPathInfo()); uriBuilder.replaceQuery(AwsProxyHttpServletRequest.decodeValueIfEncoded(servletRequest.getQueryString())); @@ -125,7 +113,7 @@ private ContainerRequest servletRequestToContainerRequest(ServletRequest request apiGatewayProperties.setProperty(JERSEY_SERVLET_REQUEST_PROPERTY, servletRequest); ContainerRequest requestContext = new ContainerRequest( - basePathUri, + URI.create(basePath), // for routing within Jersey we always assume the base path is "/" requestPathUri, servletRequest.getMethod().toUpperCase(Locale.ENGLISH), (SecurityContext)servletRequest.getAttribute(JAX_SECURITY_CONTEXT_PROPERTY), 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 bbb28853..055d6313 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 @@ -278,6 +278,30 @@ public void exception_mapException_mapToNotImplemented() { assertEquals(Response.Status.NOT_IMPLEMENTED.getStatusCode(), response.getStatusCode()); } + @Test + public void stripBasePath_route_shouldRouteCorrectly() { + AwsProxyRequest request = new AwsProxyRequestBuilder("/custompath/echo/status-code", "GET") + .json() + .queryString("status", "201") + .build(); + handler.stripBasePath("/custompath"); + AwsProxyResponse output = handler.proxy(request, lambdaContext); + assertEquals(201, output.getStatusCode()); + handler.stripBasePath(""); + } + + @Test + public void stripBasePath_route_shouldReturn404() { + AwsProxyRequest request = new AwsProxyRequestBuilder("/custompath/echo/status-code", "GET") + .json() + .queryString("status", "201") + .build(); + handler.stripBasePath("/custom"); + AwsProxyResponse output = handler.proxy(request, lambdaContext); + assertEquals(404, output.getStatusCode()); + handler.stripBasePath(""); + } + private void validateMapResponseModel(AwsProxyResponse output) { validateMapResponseModel(output, CUSTOM_HEADER_KEY, CUSTOM_HEADER_VALUE); } From 54bcef56ee1861301e8716c03a3e45916510a7c5 Mon Sep 17 00:00:00 2001 From: sapessi Date: Wed, 31 Jan 2018 14:46:45 -0800 Subject: [PATCH 0088/1214] Added unit test to validate the proposed solution for #111 and warning log messages in request object when a framework tries to access the session --- .../servlet/AwsHttpServletRequest.java | 7 ++ aws-serverless-java-container-spring/pom.xml | 67 +++++++++++++++++++ .../proxy/spring/SpringBootAppTest.java | 43 ++++++++++++ .../spring/springbootapp/LambdaHandler.java | 37 ++++++++++ .../spring/springbootapp/TestApplication.java | 12 ++++ .../spring/springbootapp/TestController.java | 30 +++++++++ 6 files changed, 196 insertions(+) create mode 100644 aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringBootAppTest.java create mode 100644 aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/springbootapp/LambdaHandler.java create mode 100644 aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/springbootapp/TestApplication.java create mode 100644 aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/springbootapp/TestController.java 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 2530d67b..b2e61430 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 @@ -107,6 +107,7 @@ public String getRequestedSessionId() { @Override public HttpSession getSession(boolean b) { + log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session"); if (b && null == this.session) { ApiGatewayRequestContext requestContext = (ApiGatewayRequestContext) getAttribute(RequestReader.API_GATEWAY_CONTEXT_PROPERTY); this.session = new AwsHttpSession(requestContext.getRequestId()); @@ -117,30 +118,35 @@ public HttpSession getSession(boolean b) { @Override public HttpSession getSession() { + log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session"); return this.session; } @Override public String changeSessionId() { + log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session"); return null; } @Override public boolean isRequestedSessionIdValid() { + log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session"); return false; } @Override public boolean isRequestedSessionIdFromCookie() { + log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session"); return false; } @Override public boolean isRequestedSessionIdFromURL() { + log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session"); return false; } @@ -148,6 +154,7 @@ public boolean isRequestedSessionIdFromURL() { @Override @Deprecated public boolean isRequestedSessionIdFromUrl() { + log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session"); return false; } diff --git a/aws-serverless-java-container-spring/pom.xml b/aws-serverless-java-container-spring/pom.xml index a0ff669b..56e71048 100644 --- a/aws-serverless-java-container-spring/pom.xml +++ b/aws-serverless-java-container-spring/pom.xml @@ -16,6 +16,7 @@ @@ -77,6 +78,72 @@ 4.3.13.RELEASE +4.2.4.RELEASE 2.9.3 2.2.4 test + +org.springframework.boot +spring-boot-autoconfigure +1.5.9.RELEASE +test ++ +org.springframework.security +spring-security-config +${spring-security.version} +test ++ ++ +org.springframework +spring-aop ++ +org.springframework +spring-expression ++ +org.springframework +spring-context ++ +org.springframework +spring-beans ++ +org.springframework +spring-core ++ org.springframework.security +spring-security-web +${spring-security.version} +test ++ ++ +org.springframework +spring-aop ++ +org.springframework +spring-expression ++ +org.springframework +spring-context ++ +org.springframework +spring-beans ++ +org.springframework +spring-core ++ +org.springframework +spring-web +diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringBootAppTest.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringBootAppTest.java new file mode 100644 index 00000000..f6829c47 --- /dev/null +++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringBootAppTest.java @@ -0,0 +1,43 @@ +package com.amazonaws.serverless.proxy.spring; + + +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.echoapp.model.SingleValueModel; +import com.amazonaws.serverless.proxy.spring.springbootapp.LambdaHandler; +import com.amazonaws.serverless.proxy.spring.springbootapp.TestController; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.*; + + +public class SpringBootAppTest { + private LambdaHandler handler = new LambdaHandler(); + private MockLambdaContext context = new MockLambdaContext(); + private ObjectMapper mapper = new ObjectMapper(); + + @Test + public void testMethod_springSecurity_doesNotThrowException() { + AwsProxyRequest req = new AwsProxyRequestBuilder("/test", "GET").build(); + AwsProxyResponse resp = handler.handleRequest(req, context); + assertNotNull(resp); + validateSingleValueModel(resp, TestController.TEST_VALUE); + } + + private void validateSingleValueModel(AwsProxyResponse output, String value) { + try { + SingleValueModel response = mapper.readValue(output.getBody(), SingleValueModel.class); + assertNotNull(response.getValue()); + assertEquals(value, response.getValue()); + } catch (IOException e) { + fail("Exception while parsing response body: " + e.getMessage()); + e.printStackTrace(); + } + } +} diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/springbootapp/LambdaHandler.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/springbootapp/LambdaHandler.java new file mode 100644 index 00000000..1451b9c0 --- /dev/null +++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/springbootapp/LambdaHandler.java @@ -0,0 +1,37 @@ +package com.amazonaws.serverless.proxy.spring.springbootapp; + + +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.serverless.proxy.spring.SpringLambdaContainerHandler; +import com.amazonaws.serverless.proxy.spring.springbootapp.TestApplication; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import org.springframework.web.context.support.XmlWebApplicationContext; + + +public class LambdaHandler + implements RequestHandler +{ + SpringBootLambdaContainerHandler handler; + boolean isinitialized = false; + + public AwsProxyResponse handleRequest(AwsProxyRequest awsProxyRequest, Context context) + { + if (!isinitialized) { + isinitialized = true; + try { + handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(TestApplication.class); + } catch (ContainerInitializationException e) { + e.printStackTrace(); + return null; + } + } + AwsProxyResponse res = handler.proxy(awsProxyRequest, context); + return res; + } +} + diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/springbootapp/TestApplication.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/springbootapp/TestApplication.java new file mode 100644 index 00000000..d203a56e --- /dev/null +++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/springbootapp/TestApplication.java @@ -0,0 +1,12 @@ +package com.amazonaws.serverless.proxy.spring.springbootapp; + + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.support.SpringBootServletInitializer; +import org.springframework.context.annotation.ComponentScan; + + +@SpringBootApplication +@ComponentScan(basePackages = "com.amazonaws.serverless.proxy.spring.springbootapp") +public class TestApplication extends SpringBootServletInitializer { +} diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/springbootapp/TestController.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/springbootapp/TestController.java new file mode 100644 index 00000000..39433db3 --- /dev/null +++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/springbootapp/TestController.java @@ -0,0 +1,30 @@ +package com.amazonaws.serverless.proxy.spring.springbootapp; + + +import com.amazonaws.serverless.proxy.spring.echoapp.model.SingleValueModel; + +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + + +@RestController +@EnableWebSecurity +public class TestController extends WebSecurityConfigurerAdapter{ + public static final String TEST_VALUE = "test"; + + @RequestMapping(path = "/test", method = { RequestMethod.GET }) + public SingleValueModel testGet() { + SingleValueModel value = new SingleValueModel(); + value.setValue(TEST_VALUE); + return value; + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.sessionManagement().disable(); + } +} From 64ab945f4e2a9c708732fbe4a9a1561e54eb5b76 Mon Sep 17 00:00:00 2001 From: sapessi Date: Wed, 31 Jan 2018 14:54:25 -0800 Subject: [PATCH 0089/1214] Changing version to 0.9.1-SNAPSHOT and preparing for release --- aws-serverless-java-container-core/pom.xml | 4 ++-- aws-serverless-java-container-jersey/pom.xml | 4 ++-- aws-serverless-java-container-spark/pom.xml | 4 ++-- aws-serverless-java-container-spring/pom.xml | 4 ++-- pom.xml | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/aws-serverless-java-container-core/pom.xml b/aws-serverless-java-container-core/pom.xml index 8cb3133f..e9c14b3b 100644 --- a/aws-serverless-java-container-core/pom.xml +++ b/aws-serverless-java-container-core/pom.xml @@ -6,12 +6,12 @@ AWS Serverless Java container support - Core Allows Java applications written for a servlet container to run in AWS Lambda https://aws.amazon.com/lambda -1.0-SNAPSHOT +0.9.1-SNAPSHOT com.amazonaws.serverless aws-serverless-java-container -1.0-SNAPSHOT +0.9.1-SNAPSHOT diff --git a/aws-serverless-java-container-jersey/pom.xml b/aws-serverless-java-container-jersey/pom.xml index 9e1532d8..d1084a04 100644 --- a/aws-serverless-java-container-jersey/pom.xml +++ b/aws-serverless-java-container-jersey/pom.xml @@ -6,12 +6,12 @@ AWS Serverless Java container support - Jersey implementation Allows Java applications written for Jersey to run in AWS Lambda https://aws.amazon.com/lambda -1.0-SNAPSHOT +0.9.1-SNAPSHOT com.amazonaws.serverless aws-serverless-java-container -1.0-SNAPSHOT +0.9.1-SNAPSHOT diff --git a/aws-serverless-java-container-spark/pom.xml b/aws-serverless-java-container-spark/pom.xml index 91738601..45aef5c7 100644 --- a/aws-serverless-java-container-spark/pom.xml +++ b/aws-serverless-java-container-spark/pom.xml @@ -6,12 +6,12 @@ AWS Serverless Java container support - Spark implementation Allows Java applications written for Spark to run in AWS Lambda https://aws.amazon.com/lambda -1.0-SNAPSHOT +0.9.1-SNAPSHOT com.amazonaws.serverless aws-serverless-java-container -1.0-SNAPSHOT +0.9.1-SNAPSHOT diff --git a/aws-serverless-java-container-spring/pom.xml b/aws-serverless-java-container-spring/pom.xml index 56e71048..338edd5c 100644 --- a/aws-serverless-java-container-spring/pom.xml +++ b/aws-serverless-java-container-spring/pom.xml @@ -6,12 +6,12 @@ AWS Serverless Java container support - Spring implementation Allows Java applications written for the Spring framework to run in AWS Lambda https://aws.amazon.com/lambda -1.0-SNAPSHOT +0.9.1-SNAPSHOT com.amazonaws.serverless aws-serverless-java-container -1.0-SNAPSHOT +0.9.1-SNAPSHOT diff --git a/pom.xml b/pom.xml index 49775a8d..0e8ab641 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.amazonaws.serverless aws-serverless-java-container pom -1.0-SNAPSHOT +0.9.1-SNAPSHOT AWS Serverless Java container From d852cc5fb86fe06e325903135026bf68b5266c0f Mon Sep 17 00:00:00 2001 From: sapessiDate: Wed, 31 Jan 2018 14:58:48 -0800 Subject: [PATCH 0090/1214] Changed dependencies to core 0.9.1-SNAPSHOT --- aws-serverless-java-container-jersey/pom.xml | 2 +- aws-serverless-java-container-spark/pom.xml | 2 +- aws-serverless-java-container-spring/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/aws-serverless-java-container-jersey/pom.xml b/aws-serverless-java-container-jersey/pom.xml index d1084a04..eb7d4ca1 100644 --- a/aws-serverless-java-container-jersey/pom.xml +++ b/aws-serverless-java-container-jersey/pom.xml @@ -23,7 +23,7 @@ com.amazonaws.serverless aws-serverless-java-container-core -1.0-SNAPSHOT +0.9.1-SNAPSHOT com.fasterxml.jackson.core diff --git a/aws-serverless-java-container-spark/pom.xml b/aws-serverless-java-container-spark/pom.xml index 45aef5c7..25c0aad5 100644 --- a/aws-serverless-java-container-spark/pom.xml +++ b/aws-serverless-java-container-spark/pom.xml @@ -23,7 +23,7 @@diff --git a/aws-serverless-java-container-spring/pom.xml b/aws-serverless-java-container-spring/pom.xml index 338edd5c..a2453f9c 100644 --- a/aws-serverless-java-container-spring/pom.xml +++ b/aws-serverless-java-container-spring/pom.xml @@ -25,7 +25,7 @@ com.amazonaws.serverless aws-serverless-java-container-core -1.0-SNAPSHOT +0.9.1-SNAPSHOT From 06dbd5294e91312a2a1ad3300e6a7602b3351c52 Mon Sep 17 00:00:00 2001 From: sapessi com.amazonaws.serverless aws-serverless-java-container-core -1.0-SNAPSHOT +0.9.1-SNAPSHOT Date: Wed, 31 Jan 2018 15:06:59 -0800 Subject: [PATCH 0091/1214] [maven-release-plugin] prepare release aws-serverless-java-container-0.9.1 --- aws-serverless-java-container-core/pom.xml | 4 ++-- aws-serverless-java-container-jersey/pom.xml | 6 +++--- aws-serverless-java-container-spark/pom.xml | 6 +++--- aws-serverless-java-container-spring/pom.xml | 6 +++--- pom.xml | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/aws-serverless-java-container-core/pom.xml b/aws-serverless-java-container-core/pom.xml index e9c14b3b..e0c9888e 100644 --- a/aws-serverless-java-container-core/pom.xml +++ b/aws-serverless-java-container-core/pom.xml @@ -6,12 +6,12 @@ AWS Serverless Java container support - Core Allows Java applications written for a servlet container to run in AWS Lambda https://aws.amazon.com/lambda -0.9.1-SNAPSHOT +0.9.1 com.amazonaws.serverless aws-serverless-java-container -0.9.1-SNAPSHOT +0.9.1 diff --git a/aws-serverless-java-container-jersey/pom.xml b/aws-serverless-java-container-jersey/pom.xml index eb7d4ca1..a42d846f 100644 --- a/aws-serverless-java-container-jersey/pom.xml +++ b/aws-serverless-java-container-jersey/pom.xml @@ -6,12 +6,12 @@ AWS Serverless Java container support - Jersey implementation Allows Java applications written for Jersey to run in AWS Lambda https://aws.amazon.com/lambda -0.9.1-SNAPSHOT +0.9.1 com.amazonaws.serverless aws-serverless-java-container -0.9.1-SNAPSHOT +0.9.1 @@ -23,7 +23,7 @@ com.amazonaws.serverless aws-serverless-java-container-core -0.9.1-SNAPSHOT +0.9.1 com.fasterxml.jackson.core diff --git a/aws-serverless-java-container-spark/pom.xml b/aws-serverless-java-container-spark/pom.xml index 25c0aad5..a83f43f4 100644 --- a/aws-serverless-java-container-spark/pom.xml +++ b/aws-serverless-java-container-spark/pom.xml @@ -6,12 +6,12 @@AWS Serverless Java container support - Spark implementation Allows Java applications written for Spark to run in AWS Lambda https://aws.amazon.com/lambda -0.9.1-SNAPSHOT +0.9.1 com.amazonaws.serverless aws-serverless-java-container -0.9.1-SNAPSHOT +0.9.1 @@ -23,7 +23,7 @@ diff --git a/aws-serverless-java-container-spring/pom.xml b/aws-serverless-java-container-spring/pom.xml index a2453f9c..050de1d5 100644 --- a/aws-serverless-java-container-spring/pom.xml +++ b/aws-serverless-java-container-spring/pom.xml @@ -6,12 +6,12 @@ com.amazonaws.serverless aws-serverless-java-container-core -0.9.1-SNAPSHOT +0.9.1 AWS Serverless Java container support - Spring implementation Allows Java applications written for the Spring framework to run in AWS Lambda https://aws.amazon.com/lambda -0.9.1-SNAPSHOT +0.9.1 com.amazonaws.serverless aws-serverless-java-container -0.9.1-SNAPSHOT +0.9.1 @@ -25,7 +25,7 @@ diff --git a/pom.xml b/pom.xml index 0e8ab641..511480ac 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.amazonaws.serverless aws-serverless-java-container-core -0.9.1-SNAPSHOT +0.9.1 com.amazonaws.serverless aws-serverless-java-container pom -0.9.1-SNAPSHOT +0.9.1 AWS Serverless Java container From 3e3481c7f678cb19e76446cc67703a574ef084d1 Mon Sep 17 00:00:00 2001 From: sapessiDate: Wed, 31 Jan 2018 15:07:00 -0800 Subject: [PATCH 0092/1214] [maven-release-plugin] prepare for next development iteration --- aws-serverless-java-container-core/pom.xml | 4 ++-- aws-serverless-java-container-jersey/pom.xml | 6 +++--- aws-serverless-java-container-spark/pom.xml | 6 +++--- aws-serverless-java-container-spring/pom.xml | 6 +++--- pom.xml | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/aws-serverless-java-container-core/pom.xml b/aws-serverless-java-container-core/pom.xml index e0c9888e..8cb3133f 100644 --- a/aws-serverless-java-container-core/pom.xml +++ b/aws-serverless-java-container-core/pom.xml @@ -6,12 +6,12 @@ AWS Serverless Java container support - Core Allows Java applications written for a servlet container to run in AWS Lambda https://aws.amazon.com/lambda -0.9.1 +1.0-SNAPSHOT com.amazonaws.serverless aws-serverless-java-container -0.9.1 +1.0-SNAPSHOT diff --git a/aws-serverless-java-container-jersey/pom.xml b/aws-serverless-java-container-jersey/pom.xml index a42d846f..9e1532d8 100644 --- a/aws-serverless-java-container-jersey/pom.xml +++ b/aws-serverless-java-container-jersey/pom.xml @@ -6,12 +6,12 @@ AWS Serverless Java container support - Jersey implementation Allows Java applications written for Jersey to run in AWS Lambda https://aws.amazon.com/lambda -0.9.1 +1.0-SNAPSHOT com.amazonaws.serverless aws-serverless-java-container -0.9.1 +1.0-SNAPSHOT @@ -23,7 +23,7 @@ com.amazonaws.serverless aws-serverless-java-container-core -0.9.1 +1.0-SNAPSHOT com.fasterxml.jackson.core diff --git a/aws-serverless-java-container-spark/pom.xml b/aws-serverless-java-container-spark/pom.xml index a83f43f4..91738601 100644 --- a/aws-serverless-java-container-spark/pom.xml +++ b/aws-serverless-java-container-spark/pom.xml @@ -6,12 +6,12 @@AWS Serverless Java container support - Spark implementation Allows Java applications written for Spark to run in AWS Lambda https://aws.amazon.com/lambda -0.9.1 +1.0-SNAPSHOT com.amazonaws.serverless aws-serverless-java-container -0.9.1 +1.0-SNAPSHOT @@ -23,7 +23,7 @@ diff --git a/aws-serverless-java-container-spring/pom.xml b/aws-serverless-java-container-spring/pom.xml index 050de1d5..56e71048 100644 --- a/aws-serverless-java-container-spring/pom.xml +++ b/aws-serverless-java-container-spring/pom.xml @@ -6,12 +6,12 @@ com.amazonaws.serverless aws-serverless-java-container-core -0.9.1 +1.0-SNAPSHOT AWS Serverless Java container support - Spring implementation Allows Java applications written for the Spring framework to run in AWS Lambda https://aws.amazon.com/lambda -0.9.1 +1.0-SNAPSHOT com.amazonaws.serverless aws-serverless-java-container -0.9.1 +1.0-SNAPSHOT @@ -25,7 +25,7 @@ diff --git a/pom.xml b/pom.xml index 511480ac..49775a8d 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.amazonaws.serverless aws-serverless-java-container-core -0.9.1 +1.0-SNAPSHOT com.amazonaws.serverless aws-serverless-java-container pom -0.9.1 +1.0-SNAPSHOT AWS Serverless Java container From b8d468e209b04a7f11633e031f3f77341425c710 Mon Sep 17 00:00:00 2001 From: sapessiDate: Wed, 31 Jan 2018 15:11:53 -0800 Subject: [PATCH 0093/1214] Added maven badge to README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 36726d72..b25ff061 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Serverless Java container [](https://travis-ci.org/awslabs/aws-serverless-java-container) [](https://gitter.im/awslabs/aws-serverless-java-container) +# Serverless Java container [](https://travis-ci.org/awslabs/aws-serverless-java-container) [](https://maven-badges.herokuapp.com/maven-central/com.amazonaws.serverless/aws-serverless-java-container) [](https://gitter.im/awslabs/aws-serverless-java-container) The `aws-serverless-java-container` makes it easy to run Java applications written with frameworks such as [Spring](https://spring.io/), [Spring Boot](https://projects.spring.io/spring-boot/), [Jersey](https://jersey.java.net/), or [Spark](http://sparkjava.com/) in [AWS Lambda](https://aws.amazon.com/lambda/). Serverless Java Container natively supports API Gateway's proxy integration models for requests and responses, you can create and inject custom models for methods that use custom mappings. From 7d4718bc2d38c95174ba4974d9aabf0a4ad70b74 Mon Sep 17 00:00:00 2001 From: sapessi Date: Thu, 1 Feb 2018 08:14:59 -0800 Subject: [PATCH 0094/1214] Removed principal logging from sample to address #104 --- .../com/amazonaws/serverless/sample/spring/PetsController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/samples/spring/pet-store/src/main/java/com/amazonaws/serverless/sample/spring/PetsController.java b/samples/spring/pet-store/src/main/java/com/amazonaws/serverless/sample/spring/PetsController.java index df622b10..a84f4232 100644 --- a/samples/spring/pet-store/src/main/java/com/amazonaws/serverless/sample/spring/PetsController.java +++ b/samples/spring/pet-store/src/main/java/com/amazonaws/serverless/sample/spring/PetsController.java @@ -37,7 +37,6 @@ public Pet createPet(@RequestBody Pet newPet) { @RequestMapping(path = "/pets", method = RequestMethod.GET) public Pet[] listPets(@RequestParam("limit") Optional limit, Principal principal) { - System.out.println(principal.getName()); int queryLimit = 10; if (limit.isPresent()) { queryLimit = limit.get(); From 65b62c277b7f24595201277a95ff82067647ac96 Mon Sep 17 00:00:00 2001 From: sapessi Date: Fri, 2 Feb 2018 10:53:30 -0800 Subject: [PATCH 0095/1214] Addressed java.lang.ClassCastException: org.springframework.boot.web.support.ErrorPageFilter cannot be cast to com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletResponse mentioned in issue #105. --- .../AwsLambdaServletContainerHandler.java | 23 +++++++++++++++++-- .../servlet/AwsProxyRequestDispatcher.java | 4 ++++ .../jersey/JerseyServletResponseWriter.java | 1 + 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java index c3909452..00489abb 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java @@ -19,6 +19,7 @@ import com.amazonaws.serverless.proxy.SecurityContextWriter; import com.amazonaws.services.lambda.runtime.Context; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,8 +27,11 @@ import javax.servlet.Servlet; import javax.servlet.ServletContext; import javax.servlet.ServletException; +import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponseWrapper; + import java.io.IOException; /** @@ -85,7 +89,7 @@ protected AwsLambdaServletContainerHandler(RequestReader onStartup Date: Fri, 2 Feb 2018 10:56:42 -0800 Subject: [PATCH 0096/1214] Addressed FB unchecked cast warnings --- .../proxy/internal/servlet/AwsProxyRequestDispatcher.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 1c719e02..8faa16c9 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 @@ -50,7 +50,6 @@ public AwsProxyRequestDispatcher(final String path, final AwsLambdaServletContai @Override @SuppressWarnings("unchecked") - @SuppressFBWarnings("BC_UNCONFIRMED_CAST") public void forward(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { if (!(servletRequest instanceof AwsProxyHttpServletRequest)) { @@ -64,13 +63,13 @@ public void forward(ServletRequest servletRequest, ServletResponse servletRespon ((AwsProxyHttpServletRequest) servletRequest).setDispatcherType(DispatcherType.FORWARD); ((AwsProxyHttpServletRequest) servletRequest).getAwsProxyRequest().setPath(dispatchPath); + assert servletResponse instanceof HttpServletResponse : servletResponse.getClass(); lambdaContainerHandler.forward((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse); } @Override @SuppressWarnings("unchecked") - @SuppressFBWarnings("BC_UNCONFIRMED_CAST") public void include(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { if (!(servletRequest instanceof AwsProxyHttpServletRequest)) { @@ -84,6 +83,7 @@ public void include(ServletRequest servletRequest, ServletResponse servletRespon ((AwsProxyHttpServletRequest) servletRequest).setDispatcherType(DispatcherType.INCLUDE); ((AwsProxyHttpServletRequest) servletRequest).getAwsProxyRequest().setPath(dispatchPath); + assert servletResponse instanceof HttpServletResponse : servletResponse.getClass(); lambdaContainerHandler.include((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse); } } From e345b1dc0f491b5b18b1be867599f1b955615374 Mon Sep 17 00:00:00 2001 From: sapessi Date: Fri, 2 Feb 2018 13:36:08 -0800 Subject: [PATCH 0097/1214] Moved the flushBuffer call on the servlet response to the handler, outside of the chain holder. This enables forwards/includes requests and partly addresses on of the issues highlighted in #105 --- .../servlet/AwsHttpServletRequest.java | 2 +- .../AwsLambdaServletContainerHandler.java | 7 ++++- .../internal/servlet/FilterChainHolder.java | 6 ----- .../servlet/AwsHttpServletResponseTest.java | 8 ++++-- .../proxy/spring/SpringBootAppTest.java | 26 +++++++++++++++++++ 5 files changed, 39 insertions(+), 10 deletions(-) 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 b2e61430..349c06e0 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 @@ -101,7 +101,7 @@ public abstract class AwsHttpServletRequest implements HttpServletRequest { @Override public String getRequestedSessionId() { - throw new UnsupportedOperationException(); + return null; } diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java index 00489abb..bce33e3a 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java @@ -19,10 +19,10 @@ import com.amazonaws.serverless.proxy.SecurityContextWriter; import com.amazonaws.services.lambda.runtime.Context; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.servlet.DispatcherType; import javax.servlet.FilterChain; import javax.servlet.Servlet; import javax.servlet.ServletContext; @@ -208,6 +208,11 @@ protected FilterChain getFilterChain(ContainerRequestType req, Servlet servlet) protected void doFilter(ContainerRequestType request, ContainerResponseType response, Servlet servlet) throws IOException, ServletException { FilterChain chain = getFilterChain(request, servlet); chain.doFilter(request, response); + + // if for some reason the response wasn't flushed yet, we force it here. + if (request.getDispatcherType() != DispatcherType.FORWARD && request.getDispatcherType() != DispatcherType.INCLUDE && !response.isCommitted()) { + response.flushBuffer(); + } } //------------------------------------------------------------- diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/FilterChainHolder.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/FilterChainHolder.java index 3b018815..682230dd 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/FilterChainHolder.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/FilterChainHolder.java @@ -17,7 +17,6 @@ import org.slf4j.LoggerFactory; import javax.servlet.*; -import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.ArrayList; @@ -86,11 +85,6 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo log.debug("Executed {}: filter {}-{}", servletRequest.getDispatcherType(), currentFilter, holder.getFilterName()); } - - // if for some reason the response wasn't flushed yet, we force it here. - if (!servletResponse.isCommitted()) { - servletResponse.flushBuffer(); - } } diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletResponseTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletResponseTest.java index d4771354..553b7c61 100644 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletResponseTest.java +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletResponseTest.java @@ -17,6 +17,9 @@ public class AwsHttpServletResponseTest { + // we use this int to compare the cookie expiration time in the tests. The date we generate to compare to + // may be slight off compared to the date generated during the request processing + private static final int COOKIE_GRACE_COMPARE_MILLIS = 2000; private static final String COOKIE_NAME = "session_id"; private static final String COOKIE_VALUE = "123"; private static final String COOKIE_PATH = "/api"; @@ -121,8 +124,9 @@ public void cookie_addCookie_positiveMaxAgeExpiresDate() { Calendar expiration = getExpires(cookieHeader); System.out.println("Cookie date: " + dateFormat.format(expiration.getTime())); System.out.println("Test date: " + dateFormat.format(testExpiration.getTime())); - // we need to compare strings because the millis time will be off - assertEquals(dateFormat.format(testExpiration.getTime()), dateFormat.format(expiration.getTime())); + + long dateDiff = testExpiration.getTimeInMillis() - expiration.getTimeInMillis(); + assertTrue(Math.abs(dateDiff) < COOKIE_GRACE_COMPARE_MILLIS); } @Test diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringBootAppTest.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringBootAppTest.java index f6829c47..5fdabec6 100644 --- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringBootAppTest.java +++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringBootAppTest.java @@ -9,10 +9,13 @@ import com.amazonaws.serverless.proxy.spring.springbootapp.LambdaHandler; import com.amazonaws.serverless.proxy.spring.springbootapp.TestController; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.Test; import java.io.IOException; +import java.util.Map; import static org.junit.Assert.*; @@ -30,6 +33,29 @@ public void testMethod_springSecurity_doesNotThrowException() { validateSingleValueModel(resp, TestController.TEST_VALUE); } + @Test + public void defaultError_requestForward_springBootForwardsToDefaultErrorPage() { + AwsProxyRequest req = new AwsProxyRequestBuilder("/test2", "GET").build(); + AwsProxyResponse resp = handler.handleRequest(req, context); + assertNotNull(resp); + assertEquals(404, resp.getStatusCode()); + assertNotNull(resp.getHeaders()); + assertTrue(resp.getHeaders().containsKey("Content-Type")); + assertEquals("application/json;charset=UTF-8", resp.getHeaders().get("Content-Type")); + try { + JsonNode errorData = mapper.readTree(resp.getBody()); + assertNotNull(errorData.findValue("status")); + assertEquals(404, errorData.findValue("status").asInt()); + assertNotNull(errorData.findValue("message")); + assertEquals("No message available", errorData.findValue("message").asText()); + + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + + } + private void validateSingleValueModel(AwsProxyResponse output, String value) { try { SingleValueModel response = mapper.readValue(output.getBody(), SingleValueModel.class); From c70c4dcc4b55f25f1e0e665cb6d468b8eba4f725 Mon Sep 17 00:00:00 2001 From: sapessi Date: Fri, 9 Feb 2018 11:59:39 -0800 Subject: [PATCH 0098/1214] Added request logging from #116. Includes a LogFormatter interface and its default implementation --- .../serverless/proxy/LogFormatter.java | 24 ++++ .../internal/LambdaContainerHandler.java | 17 +++ .../ApacheCombinedServletLogFormatter.java | 105 ++++++++++++++++++ .../AwsLambdaServletContainerHandler.java | 3 + .../servlet/AwsProxyHttpServletRequest.java | 11 +- .../testutils/AwsProxyRequestBuilder.java | 25 +++++ .../proxy/model/ApiGatewayRequestContext.java | 11 ++ .../AwsProxyHttpServletRequestTest.java | 17 +++ 8 files changed, 211 insertions(+), 2 deletions(-) create mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/LogFormatter.java create mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ApacheCombinedServletLogFormatter.java diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/LogFormatter.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/LogFormatter.java new file mode 100644 index 00000000..cef0514d --- /dev/null +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/LogFormatter.java @@ -0,0 +1,24 @@ +package com.amazonaws.serverless.proxy; + + +import javax.ws.rs.core.SecurityContext; + + +/** + * Implementations of the log formatter interface are used by {@link com.amazonaws.serverless.proxy.internal.LambdaContainerHandler} class to log each request + * processed in the container. You can set the log formatter using the {@link com.amazonaws.serverless.proxy.internal.LambdaContainerHandler#setLogFormatter(LogFormatter)} + * method. The servlet implementation of the container ({@link com.amazonaws.serverless.proxy.internal.servlet.AwsLambdaServletContainerHandler} includes a + * default log formatter that produces Apache combined logs. {@link com.amazonaws.serverless.proxy.internal.servlet.ApacheCombinedServletLogFormatter}. + * @param The request type used by the underlying framework + * @param The response type produced by the underlying framework + */ +public interface LogFormatter { + /** + * The format method is called by the container handler to produce the log line that should be written to the logs. + * @param req The incoming request + * @param res The completed response + * @param ctx The security context produced based on the request + * @return The log line + */ + String format(ContainerRequestType req, ContainerResponseType res, SecurityContext ctx); +} diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandler.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandler.java index d9729a22..7f5fd4ea 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandler.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandler.java @@ -13,6 +13,8 @@ package com.amazonaws.serverless.proxy.internal; +import com.amazonaws.serverless.proxy.LogFormatter; +import com.amazonaws.serverless.proxy.internal.servlet.ApacheCombinedServletLogFormatter; import com.amazonaws.serverless.proxy.model.ContainerConfig; import com.amazonaws.serverless.proxy.ExceptionHandler; import com.amazonaws.serverless.proxy.RequestReader; @@ -56,10 +58,12 @@ public abstract class LambdaContainerHandler exceptionHandler; protected Context lambdaContext; + protected LogFormatter logFormatter; private Logger log = LoggerFactory.getLogger(LambdaContainerHandler.class); + //------------------------------------------------------------- // Variables - Private - Static //------------------------------------------------------------- @@ -119,6 +123,15 @@ public void stripBasePath(String basePath) { config.setServiceBasePath(basePath); } + /** + * Sets the formatter used to log request data in CloudWatch. By default this is set to use an Apache + * combined log format based on the servlet request and response object {@link ApacheCombinedServletLogFormatter}. + * @param formatter The log formatter object + */ + public void setLogFormatter(LogFormatter formatter) { + this.logFormatter = formatter; + } + /** * Proxies requests to the underlying container given the incoming Lambda request. This method returns a populated @@ -140,6 +153,10 @@ public ResponseType proxy(RequestType request, Context context) { latch.await(); + if (logFormatter != null) { + log.info(SecurityUtils.crlf(logFormatter.format(containerRequest, containerResponse, securityContext))); + } + return responseWriter.writeResponse(containerResponse, context); } catch (Exception e) { log.error("Error while handling request", e); 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 new file mode 100644 index 00000000..b43e2169 --- /dev/null +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/ApacheCombinedServletLogFormatter.java @@ -0,0 +1,105 @@ +package com.amazonaws.serverless.proxy.internal.servlet; + + +import com.amazonaws.serverless.proxy.LogFormatter; +import com.amazonaws.serverless.proxy.model.ApiGatewayRequestContext; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.SecurityContext; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Locale; + +import static com.amazonaws.serverless.proxy.RequestReader.API_GATEWAY_CONTEXT_PROPERTY; + + +/** + * Default implementation of the log formatter. Based on an HttpServletRequestandHttpServletResponseimplementations produced + * a log line in the Apache combined log format: https://httpd.apache.org/docs/2.4/logs.html + * @paramAn implementation of HttpServletRequest+ * @paramAn implementation of HttpServletResponse+ */ +public class ApacheCombinedServletLogFormatter+ implements LogFormatter { + SimpleDateFormat dateFormat; + + public ApacheCombinedServletLogFormatter() { + dateFormat = new SimpleDateFormat("[dd/MM/yyyy:hh:mm:ss Z]"); + } + + @Override + @SuppressFBWarnings({ "SERVLET_HEADER_REFERER", "SERVLET_HEADER_USER_AGENT" }) + 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(); + + // %h + logLineBuilder.append(servletRequest.getRemoteAddr()); + logLineBuilder.append(" "); + + // %l + if (servletRequest instanceof AwsProxyHttpServletRequest && servletRequest.getAttribute(API_GATEWAY_CONTEXT_PROPERTY) != null) { + ApiGatewayRequestContext gatewayContext = (ApiGatewayRequestContext)servletRequest.getAttribute(API_GATEWAY_CONTEXT_PROPERTY); + logLineBuilder.append(gatewayContext.getIdentity().getUserArn()); + logLineBuilder.append(" "); + } else { + logLineBuilder.append("- "); + } + + // %u + if (ctx != null) { + logLineBuilder.append(ctx.getUserPrincipal().getName()); + } + logLineBuilder.append(" "); + + + // %t + logLineBuilder.append(dateFormat.format(Calendar.getInstance().getTime())); + logLineBuilder.append(" "); + + // %r + logLineBuilder.append("\""); + logLineBuilder.append(servletRequest.getMethod().toUpperCase(Locale.ENGLISH)); + logLineBuilder.append(" "); + logLineBuilder.append(servletRequest.getPathInfo()); + logLineBuilder.append(" "); + logLineBuilder.append(servletRequest.getProtocol()); + logLineBuilder.append(" \" "); + + // %>s + logLineBuilder.append(servletResponse.getStatus()); + logLineBuilder.append(" "); + + // %b + if (servletResponse instanceof AwsHttpServletResponse) { + AwsHttpServletResponse awsResponse = (AwsHttpServletResponse)servletResponse; + if (awsResponse.getAwsResponseBodyBytes().length > 0) { + logLineBuilder.append(awsResponse.getAwsResponseBodyBytes().length); + } else { + logLineBuilder.append("-"); + } + } else { + logLineBuilder.append("-"); + } + logLineBuilder.append(" "); + + // \"%{Referer}i\" + logLineBuilder.append("\""); + logLineBuilder.append(servletRequest.getHeader("referer")); + logLineBuilder.append("\""); + + // \"%{User-agent}i\" + logLineBuilder.append("\""); + logLineBuilder.append(servletRequest.getHeader("user-agent")); + logLineBuilder.append("\""); + + logLineBuilder.append(" combined"); + + + return logLineBuilder.toString(); + } +} diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java index bce33e3a..fa29e94e 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java @@ -34,6 +34,7 @@ import java.io.IOException; + /** * Abstract extension of the code LambdaContainerHandlerobject that adds protected variables for the *ServletContextandFilterChainManager. This object should be extended by the framework-specific @@ -73,6 +74,8 @@ protected AwsLambdaServletContainerHandler(RequestReadersecurityContextWriter, ExceptionHandler exceptionHandler) { super(requestReader, responseWriter, securityContextWriter, exceptionHandler); + // set the default log formatter for servlet implementations + setLogFormatter(new ApacheCombinedServletLogFormatter<>()); } //------------------------------------------------------------- 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 a964a1b4..9c8a19ed 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 @@ -501,8 +501,7 @@ public Map getParameterMap() { @Override public String getProtocol() { - // TODO: We should have a cloudfront protocol header - return null; + return request.getRequestContext().getProtocol(); } @@ -621,6 +620,14 @@ public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse se private String getHeaderCaseInsensitive(String key) { + // special cases for referer and user agent headers + if ("referer".equals(key.toLowerCase(Locale.ENGLISH))) { + return request.getRequestContext().getIdentity().getCaller(); + } + if ("user-agent".equals(key.toLowerCase(Locale.ENGLISH))) { + return request.getRequestContext().getIdentity().getUserAgent(); + } + if (request.getHeaders() == null) { return null; } diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java index b9c839f1..abda6933 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java @@ -68,6 +68,7 @@ public AwsProxyRequestBuilder(String path, String httpMethod) { this.request.setRequestContext(new ApiGatewayRequestContext()); this.request.getRequestContext().setRequestId("test-invoke-request"); this.request.getRequestContext().setStage("test"); + this.request.getRequestContext().setProtocol("HTTP/1.1"); ApiGatewayRequestIdentity identity = new ApiGatewayRequestIdentity(); identity.setSourceIp("127.0.0.1"); this.request.getRequestContext().setIdentity(identity); @@ -236,6 +237,30 @@ public AwsProxyRequestBuilder serverName(String serverName) { return this; } + public AwsProxyRequestBuilder userAgent(String agent) { + if (request.getRequestContext() == null) { + request.setRequestContext(new ApiGatewayRequestContext()); + } + if (request.getRequestContext().getIdentity() == null) { + request.getRequestContext().setIdentity(new ApiGatewayRequestIdentity()); + } + + request.getRequestContext().getIdentity().setUserAgent(agent); + return this; + } + + public AwsProxyRequestBuilder referer(String referer) { + if (request.getRequestContext() == null) { + request.setRequestContext(new ApiGatewayRequestContext()); + } + if (request.getRequestContext().getIdentity() == null) { + request.getRequestContext().setIdentity(new ApiGatewayRequestIdentity()); + } + + request.getRequestContext().getIdentity().setCaller(referer); + return this; + } + public AwsProxyRequestBuilder fromJsonString(String jsonContent) throws IOException { request = LambdaContainerHandler.getObjectMapper().readValue(jsonContent, AwsProxyRequest.class); diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/ApiGatewayRequestContext.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/ApiGatewayRequestContext.java index 657bde19..8f52f542 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/ApiGatewayRequestContext.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/ApiGatewayRequestContext.java @@ -43,6 +43,7 @@ public class ApiGatewayRequestContext { private ApiGatewayAuthorizerContext authorizer; private String stage; private String path; + private String protocol; //------------------------------------------------------------- @@ -146,4 +147,14 @@ public ApiGatewayAuthorizerContext getAuthorizer() { public void setAuthorizer(ApiGatewayAuthorizerContext authorizer) { this.authorizer = authorizer; } + + + public String getProtocol() { + return protocol; + } + + + public void setProtocol(String protocol) { + this.protocol = protocol; + } } 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 fc104432..b2342efe 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 @@ -23,6 +23,8 @@ public class AwsProxyHttpServletRequestTest { private static final String FORM_PARAM_TEST = "test_cookie_param"; private static final String QUERY_STRING_NAME_VALUE = "Bob"; private static final String REQUEST_SCHEME_HTTP = "http"; + private static final String USER_AGENT = "Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0"; + private static final String REFERER = "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent/Firefox"; private static final AwsProxyRequest REQUEST_WITH_HEADERS = new AwsProxyRequestBuilder("/hello", "GET") .header(CUSTOM_HEADER_KEY, CUSTOM_HEADER_VALUE) @@ -46,6 +48,9 @@ public class AwsProxyHttpServletRequestTest { private static final AwsProxyRequest REQUEST_MULTIPLE_FORM_AND_QUERY = new AwsProxyRequestBuilder("/hello", "POST") .form(FORM_PARAM_NAME, FORM_PARAM_NAME_VALUE) .queryString(FORM_PARAM_TEST, QUERY_STRING_NAME_VALUE).build(); + private static final AwsProxyRequest REQUEST_USER_AGENT_REFERER = new AwsProxyRequestBuilder("/hello", "POST") + .userAgent(USER_AGENT) + .referer(REFERER).build(); private static final AwsProxyRequest REQUEST_NULL_QUERY_STRING; static { @@ -66,6 +71,18 @@ public void headers_getHeader_validRequest() { assertEquals(MediaType.APPLICATION_JSON, request.getContentType()); } + @Test + public void headers_getRefererAndUserAgent_returnsContextValues() { + HttpServletRequest request = new AwsProxyHttpServletRequest(REQUEST_USER_AGENT_REFERER, null, null); + assertNotNull(request.getHeader("Referer")); + assertEquals(REFERER, request.getHeader("Referer")); + assertEquals(REFERER, request.getHeader("referer")); + + assertNotNull(request.getHeader("User-Agent")); + assertEquals(USER_AGENT, request.getHeader("User-Agent")); + assertEquals(USER_AGENT, request.getHeader("user-agent")); + } + @Test public void formParams_getParameter_validForm() { HttpServletRequest request = new AwsProxyHttpServletRequest(REQUEST_FORM_URLENCODED, null, null); From 62d96002174ff1930055551dc212b4e8c75e9f27 Mon Sep 17 00:00:00 2001 From: sapessi Date: Fri, 9 Feb 2018 13:06:23 -0800 Subject: [PATCH 0099/1214] Added new fields to request context and fixed a couple of issues with log formatter for #116 --- .../ApacheCombinedServletLogFormatter.java | 46 +++++++++++++------ .../proxy/model/ApiGatewayRequestContext.java | 22 +++++++++ 2 files changed, 54 insertions(+), 14 deletions(-) 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 b43e2169..66b8fc1b 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 @@ -11,7 +11,9 @@ import javax.ws.rs.core.SecurityContext; import java.text.SimpleDateFormat; +import java.time.Instant; import java.util.Calendar; +import java.util.Date; import java.util.Locale; import static com.amazonaws.serverless.proxy.RequestReader.API_GATEWAY_CONTEXT_PROPERTY; @@ -36,29 +38,37 @@ 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(); + ApiGatewayRequestContext gatewayContext = (ApiGatewayRequestContext)servletRequest.getAttribute(API_GATEWAY_CONTEXT_PROPERTY); // %h logLineBuilder.append(servletRequest.getRemoteAddr()); logLineBuilder.append(" "); // %l - if (servletRequest instanceof AwsProxyHttpServletRequest && servletRequest.getAttribute(API_GATEWAY_CONTEXT_PROPERTY) != null) { - ApiGatewayRequestContext gatewayContext = (ApiGatewayRequestContext)servletRequest.getAttribute(API_GATEWAY_CONTEXT_PROPERTY); - logLineBuilder.append(gatewayContext.getIdentity().getUserArn()); - logLineBuilder.append(" "); + if (gatewayContext != null) { + if (gatewayContext.getIdentity().getUserArn() != null) { + logLineBuilder.append(gatewayContext.getIdentity().getUserArn()); + } else { + logLineBuilder.append("-"); + } } else { - logLineBuilder.append("- "); + logLineBuilder.append("-"); } + logLineBuilder.append(" "); // %u - if (ctx != null) { + if (ctx != null && ctx.getUserPrincipal().getName() != null) { logLineBuilder.append(ctx.getUserPrincipal().getName()); + logLineBuilder.append(" "); } - logLineBuilder.append(" "); // %t - logLineBuilder.append(dateFormat.format(Calendar.getInstance().getTime())); + if (gatewayContext != null) { + logLineBuilder.append(dateFormat.format(Date.from(Instant.ofEpochMilli(gatewayContext.getRequestTimeEpoch())))); + } else { + logLineBuilder.append(dateFormat.format(Calendar.getInstance().getTime())); + } logLineBuilder.append(" "); // %r @@ -68,7 +78,7 @@ public String format(ContainerRequestType servletRequest, ContainerResponseType logLineBuilder.append(servletRequest.getPathInfo()); logLineBuilder.append(" "); logLineBuilder.append(servletRequest.getProtocol()); - logLineBuilder.append(" \" "); + logLineBuilder.append("\" "); // %>s logLineBuilder.append(servletResponse.getStatus()); @@ -89,15 +99,23 @@ public String format(ContainerRequestType servletRequest, ContainerResponseType // \"%{Referer}i\" logLineBuilder.append("\""); - logLineBuilder.append(servletRequest.getHeader("referer")); - logLineBuilder.append("\""); + if (servletRequest.getHeader("referer") != null) { + logLineBuilder.append(servletRequest.getHeader("referer")); + } else { + logLineBuilder.append("-"); + } + logLineBuilder.append("\" "); // \"%{User-agent}i\" logLineBuilder.append("\""); - logLineBuilder.append(servletRequest.getHeader("user-agent")); - logLineBuilder.append("\""); + if (servletRequest.getHeader("user-agent") != null) { + logLineBuilder.append(servletRequest.getHeader("user-agent")); + } else { + logLineBuilder.append("-"); + } + logLineBuilder.append("\" "); - logLineBuilder.append(" combined"); + logLineBuilder.append("combined"); return logLineBuilder.toString(); diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/ApiGatewayRequestContext.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/ApiGatewayRequestContext.java index 8f52f542..edf28e05 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/ApiGatewayRequestContext.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/ApiGatewayRequestContext.java @@ -44,6 +44,8 @@ public class ApiGatewayRequestContext { private String stage; private String path; private String protocol; + private String requestTime; + private long requestTimeEpoch; //------------------------------------------------------------- @@ -157,4 +159,24 @@ public String getProtocol() { 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; + } } From 1ad723f6c89b47b838ecdbfa190045c64eef25d8 Mon Sep 17 00:00:00 2001 From: sapessi Date: Fri, 9 Feb 2018 13:11:27 -0800 Subject: [PATCH 0100/1214] Changed warning messages when trying to access the session to debug to avoid cluttering CloudWatch logs when used with Spring --- .../internal/servlet/AwsHttpServletRequest.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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 349c06e0..67c962c2 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 @@ -107,7 +107,7 @@ public String getRequestedSessionId() { @Override public HttpSession getSession(boolean b) { - log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session"); + log.debug("Trying to access session. Lambda functions are stateless and should not rely on the session"); if (b && null == this.session) { ApiGatewayRequestContext requestContext = (ApiGatewayRequestContext) getAttribute(RequestReader.API_GATEWAY_CONTEXT_PROPERTY); this.session = new AwsHttpSession(requestContext.getRequestId()); @@ -118,35 +118,35 @@ public HttpSession getSession(boolean b) { @Override public HttpSession getSession() { - log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session"); + log.debug("Trying to access session. Lambda functions are stateless and should not rely on the session"); return this.session; } @Override public String changeSessionId() { - log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session"); + log.debug("Trying to access session. Lambda functions are stateless and should not rely on the session"); return null; } @Override public boolean isRequestedSessionIdValid() { - log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session"); + log.debug("Trying to access session. Lambda functions are stateless and should not rely on the session"); return false; } @Override public boolean isRequestedSessionIdFromCookie() { - log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session"); + log.debug("Trying to access session. Lambda functions are stateless and should not rely on the session"); return false; } @Override public boolean isRequestedSessionIdFromURL() { - log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session"); + log.debug("Trying to access session. Lambda functions are stateless and should not rely on the session"); return false; } @@ -154,7 +154,7 @@ public boolean isRequestedSessionIdFromURL() { @Override @Deprecated public boolean isRequestedSessionIdFromUrl() { - log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session"); + log.debug("Trying to access session. Lambda functions are stateless and should not rely on the session"); return false; } From da248d9f9a24f6ecc8cf796836359e63f0ae99ec Mon Sep 17 00:00:00 2001 From: sapessi Date: Fri, 9 Feb 2018 13:29:20 -0800 Subject: [PATCH 0101/1214] Moved from findbugs to spotbugs (#120) and addresse new issue discovered by spotbugs --- aws-serverless-java-container-core/pom.xml | 40 +++---------------- .../servlet/AwsHttpServletRequest.java | 4 +- aws-serverless-java-container-jersey/pom.xml | 40 +++---------------- aws-serverless-java-container-spark/pom.xml | 40 +++---------------- aws-serverless-java-container-spring/pom.xml | 40 +++---------------- 5 files changed, 22 insertions(+), 142 deletions(-) diff --git a/aws-serverless-java-container-core/pom.xml b/aws-serverless-java-container-core/pom.xml index 8cb3133f..7fac3be7 100644 --- a/aws-serverless-java-container-core/pom.xml +++ b/aws-serverless-java-container-core/pom.xml @@ -65,42 +65,12 @@ - - -- -- -org.codehaus.mojo -findbugs-maven-plugin -3.0.5 -- - -Max - -Low - -true - -- -- -com.h3xstream.findsecbugs -findsecbugs-plugin -1.7.1 -- org.codehaus.mojo -findbugs-maven-plugin -3.0.5 +com.github.spotbugs +spotbugs-maven-plugin +3.1.1 true -${project.build.directory}/findbugs +${project.build.directory}/spotbugs @@ -125,7 +95,7 @@ analyze-compile 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 67c962c2..925ce973 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 @@ -275,7 +275,7 @@ protected Cookie[] parseCookieHeaderValue(String headerValue) { return parsedHeaders.stream() .filter(e -> e.getKey() != null) - .map(e -> new Cookie(e.getKey(), e.getValue())) + .map(e -> new Cookie(SecurityUtils.crlf(e.getKey()), SecurityUtils.crlf(e.getValue()))) .toArray(Cookie[]::new); } @@ -304,7 +304,7 @@ protected String generateQueryString(Mapparameters) { newValue = URLEncoder.encode(newValue, StandardCharsets.UTF_8.name()); } } catch (UnsupportedEncodingException e) { - log.error("Could not URLEncode: " + newKey, e); + log.error(SecurityUtils.crlf("Could not URLEncode: " + newKey), e); } return newKey + "=" + newValue; diff --git a/aws-serverless-java-container-jersey/pom.xml b/aws-serverless-java-container-jersey/pom.xml index 9e1532d8..bcdbdcbf 100644 --- a/aws-serverless-java-container-jersey/pom.xml +++ b/aws-serverless-java-container-jersey/pom.xml @@ -70,42 +70,12 @@ - - -- -- -org.codehaus.mojo -findbugs-maven-plugin -3.0.5 -- - -Max - -Low - -true - -- -- -com.h3xstream.findsecbugs -findsecbugs-plugin -1.7.1 -- org.codehaus.mojo -findbugs-maven-plugin -3.0.5 +com.github.spotbugs +spotbugs-maven-plugin +3.1.1 true -${project.build.directory}/findbugs +${project.build.directory}/spotbugs @@ -130,7 +100,7 @@ analyze-compile diff --git a/aws-serverless-java-container-spark/pom.xml b/aws-serverless-java-container-spark/pom.xml index 91738601..f534b090 100644 --- a/aws-serverless-java-container-spark/pom.xml +++ b/aws-serverless-java-container-spark/pom.xml @@ -41,36 +41,6 @@ -- -- -- -org.codehaus.mojo -findbugs-maven-plugin -3.0.5 -- - -Max - -Low - -true - -- -- -com.h3xstream.findsecbugs -findsecbugs-plugin -1.7.1 -@@ -83,9 +53,9 @@ - org.codehaus.mojo -findbugs-maven-plugin -3.0.5 +com.github.spotbugs +spotbugs-maven-plugin +3.1.1 true -${project.build.directory}/findbugs +${project.build.directory}/spotbugs @@ -110,7 +80,7 @@ analyze-compile diff --git a/aws-serverless-java-container-spring/pom.xml b/aws-serverless-java-container-spring/pom.xml index 56e71048..9814c0f8 100644 --- a/aws-serverless-java-container-spring/pom.xml +++ b/aws-serverless-java-container-spring/pom.xml @@ -146,36 +146,6 @@ -- -- -- -org.codehaus.mojo -findbugs-maven-plugin -3.0.5 -- - -Max - -Low - -true - -- -- -com.h3xstream.findsecbugs -findsecbugs-plugin -1.7.1 -@@ -188,9 +158,9 @@ - org.codehaus.mojo -findbugs-maven-plugin -3.0.5 +com.github.spotbugs +spotbugs-maven-plugin +3.1.1 true -${project.build.directory}/findbugs +${project.build.directory}/spotbugs @@ -215,7 +185,7 @@ analyze-compile From 361ae5a1646916291f0bc08105b245cada2f0512 Mon Sep 17 00:00:00 2001 From: sapessiDate: Fri, 9 Feb 2018 17:49:04 -0800 Subject: [PATCH 0102/1214] Added method to simplify the implementation of Lambda's . This addresses #118. --- .../internal/LambdaContainerHandler.java | 40 ++++++- .../AwsLambdaServletContainerHandler.java | 5 +- .../testutils/AwsProxyRequestBuilder.java | 12 ++ .../jersey/JerseyLambdaContainerHandler.java | 9 +- .../spark/SparkLambdaContainerHandler.java | 8 +- .../spark/HelloWorldSparkStreamTest.java | 108 ++++++++++++++++++ .../proxy/spark/InitExceptionHandlerTest.java | 12 +- .../SpringBootLambdaContainerHandler.java | 7 +- .../spring/SpringLambdaContainerHandler.java | 8 +- .../sample/jersey/StreamLambdaHandler.java | 13 +-- .../sample/spark/StreamLambdaHandler.java | 13 +-- .../sample/spring/StreamLambdaHandler.java | 9 +- .../springboot/StreamLambdaHandler.java | 9 +- 13 files changed, 197 insertions(+), 56 deletions(-) create mode 100644 aws-serverless-java-container-spark/src/test/java/com/amazonaws/serverless/proxy/spark/HelloWorldSparkStreamTest.java diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandler.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandler.java index 7f5fd4ea..9e268d2f 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandler.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandler.java @@ -22,12 +22,17 @@ import com.amazonaws.serverless.proxy.SecurityContextWriter; import com.amazonaws.services.lambda.runtime.Context; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.ws.rs.core.SecurityContext; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.concurrent.CountDownLatch; @@ -56,9 +61,10 @@ public abstract class LambdaContainerHandler responseWriter; private SecurityContextWriter securityContextWriter; private ExceptionHandler exceptionHandler; + private Class requestTypeClass; protected Context lambdaContext; - protected LogFormatter logFormatter; + private LogFormatter logFormatter; private Logger log = LoggerFactory.getLogger(LambdaContainerHandler.class); @@ -77,11 +83,13 @@ public abstract class LambdaContainerHandler requestReader, + protected LambdaContainerHandler(Class requestClass, + RequestReader requestReader, ResponseWriter responseWriter, SecurityContextWriter securityContextWriter, ExceptionHandler exceptionHandler) { log.info("Starting Lambda Container Handler"); + requestTypeClass = requestClass; this.requestReader = requestReader; this.responseWriter = responseWriter; this.securityContextWriter = securityContextWriter; @@ -166,6 +174,34 @@ public ResponseType proxy(RequestType request, Context context) { } + /** + * Handles Lambda RequestStreamHandlermethod. The method uses anObjectMapper+ * to transform the incoming input stream into the given {@link RequestType} and then calls the + * {@link #proxy(Object, Context)} method to handle the request. The output from the proxy method is + * written on the given output stream. + * @param input Lambda's incoming input stream + * @param output Lambda's response output stream + * @param context Lambda's context object + * @throws IOException If an error occurs during the stream processing + */ + public void proxyStream(InputStream input, OutputStream output, Context context) + throws IOException { + + try { + RequestType request = getObjectMapper().readValue(input, requestTypeClass); + ResponseType resp = proxy(request, context); + + getObjectMapper().writeValue(output, resp); + } catch (JsonParseException e) { + log.error("Error while parsing request object stream", e); + getObjectMapper().writeValue(output, exceptionHandler.handle(e)); + } catch (JsonMappingException e) { + log.error("Error while mapping object to RequestType class", e); + getObjectMapper().writeValue(output, exceptionHandler.handle(e)); + } + } + + //------------------------------------------------------------- // Methods - Getter/Setter //------------------------------------------------------------- diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java index fa29e94e..4839ce7d 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java @@ -69,11 +69,12 @@ public abstract class AwsLambdaServletContainerHandlerrequestReader, + protected AwsLambdaServletContainerHandler(Class requestTypeClass, + RequestReader requestReader, ResponseWriter responseWriter, SecurityContextWriter securityContextWriter, ExceptionHandler exceptionHandler) { - super(requestReader, responseWriter, securityContextWriter, exceptionHandler); + super(requestTypeClass, requestReader, responseWriter, securityContextWriter, exceptionHandler); // set the default log formatter for servlet implementations setLogFormatter(new ApacheCombinedServletLogFormatter<>()); } diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java index abda6933..6b88074c 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java @@ -27,9 +27,12 @@ import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.HashMap; @@ -277,4 +280,13 @@ public AwsProxyRequestBuilder fromJsonPath(String filePath) public AwsProxyRequest build() { return this.request; } + + public InputStream buildStream() { + try { + String requestJson = LambdaContainerHandler.getObjectMapper().writeValueAsString(request); + return new ByteArrayInputStream(requestJson.getBytes(StandardCharsets.UTF_8)); + } catch (JsonProcessingException e) { + return null; + } + } } 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 ec34f7d0..3caef5fe 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 @@ -95,7 +95,8 @@ public class JerseyLambdaContainerHandler extends Aws * @return A JerseyLambdaContainerHandlerobject */ public static JerseyLambdaContainerHandlergetAwsProxyHandler(Application jaxRsApplication) { - return new JerseyLambdaContainerHandler<>(new AwsProxyHttpServletRequestReader(), + return new JerseyLambdaContainerHandler<>(AwsProxyRequest.class, + new AwsProxyHttpServletRequestReader(), new AwsProxyHttpServletResponseWriter(), new AwsProxySecurityContextWriter(), new AwsProxyExceptionHandler(), @@ -110,19 +111,21 @@ public static JerseyLambdaContainerHandler ge /** * Private constructor for a LambdaContainer. Sets the application object, sets the ApplicationHandler, * and initializes the application using the onStartupmethod. + * @param requestTypeClass The class for the expected event type * @param requestReader A request reader instance * @param responseWriter A response writer instance * @param securityContextWriter A security context writer object * @param exceptionHandler An exception handler * @param jaxRsApplication The JaxRs application */ - public JerseyLambdaContainerHandler(RequestReaderrequestReader, + public JerseyLambdaContainerHandler(Class requestTypeClass, + RequestReader requestReader, ResponseWriter responseWriter, SecurityContextWriter securityContextWriter, ExceptionHandler exceptionHandler, Application jaxRsApplication) { - super(requestReader, responseWriter, securityContextWriter, exceptionHandler); + super(requestTypeClass, requestReader, responseWriter, securityContextWriter, exceptionHandler); Timer.start("JERSEY_CONTAINER_CONSTRUCTOR"); this.jaxRsApplication = jaxRsApplication; this.initialized = false; 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 300dc57e..b4d6a53a 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 @@ -106,7 +106,8 @@ public class SparkLambdaContainerHandler */ public static SparkLambdaContainerHandler getAwsProxyHandler() throws ContainerInitializationException { - return new SparkLambdaContainerHandler<>(new AwsProxyHttpServletRequestReader(), + return new SparkLambdaContainerHandler<>(AwsProxyRequest.class, + new AwsProxyHttpServletRequestReader(), new AwsProxyHttpServletResponseWriter(), new AwsProxySecurityContextWriter(), new AwsProxyExceptionHandler(), @@ -118,13 +119,14 @@ public static SparkLambdaContainerHandler get //------------------------------------------------------------- - public SparkLambdaContainerHandler(RequestReader requestReader, + public SparkLambdaContainerHandler(Class requestTypeClass, + RequestReader requestReader, ResponseWriter responseWriter, SecurityContextWriter securityContextWriter, ExceptionHandler exceptionHandler, LambdaEmbeddedServerFactory embeddedServerFactory) throws ContainerInitializationException { - super(requestReader, responseWriter, securityContextWriter, exceptionHandler); + super(requestTypeClass, requestReader, responseWriter, securityContextWriter, exceptionHandler); Timer.start("SPARK_CONTAINER_HANDLER_CONSTRUCTOR"); EmbeddedServers.add(LAMBDA_EMBEDDED_SERVER_CODE, embeddedServerFactory); 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 new file mode 100644 index 00000000..cbcfb377 --- /dev/null +++ b/aws-serverless-java-container-spark/src/test/java/com/amazonaws/serverless/proxy/spark/HelloWorldSparkStreamTest.java @@ -0,0 +1,108 @@ +package com.amazonaws.serverless.proxy.spark; + + +import com.amazonaws.serverless.exceptions.ContainerInitializationException; +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 org.apache.commons.io.IOUtils; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import spark.Spark; + +import javax.servlet.http.Cookie; +import javax.ws.rs.core.HttpHeaders; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static spark.Spark.get; + +// This class doesn't actually test Spark. Instead it tests the proxyStream method of the +// LambdaContainerHandler object. We use the Spark implementation for this because it's the +// fastest to start +public class HelloWorldSparkStreamTest { + private static final String CUSTOM_HEADER_KEY = "X-Custom-Header"; + private static final String CUSTOM_HEADER_VALUE = "My Header Value"; + private static final String BODY_TEXT_RESPONSE = "Hello World"; + + private static final String COOKIE_NAME = "MyCookie"; + private static final String COOKIE_VALUE = "CookieValue"; + private static final String COOKIE_DOMAIN = "mydomain.com"; + private static final String COOKIE_PATH = "/"; + + private static SparkLambdaContainerHandler handler; + + @BeforeClass + public static void initializeServer() { + try { + handler = SparkLambdaContainerHandler.getAwsProxyHandler(); + + configureRoutes(); + Spark.awaitInitialization(); + } catch (RuntimeException | ContainerInitializationException e) { + e.printStackTrace(); + fail(); + } + } + + @AfterClass + public static void stopSpark() { + Spark.stop(); + } + + @Test + public void helloRequest_basicStream_populatesOutputSuccessfully() { + InputStream req = new AwsProxyRequestBuilder().method("GET").path("/hello").buildStream(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try { + handler.proxyStream(req, outputStream, new MockLambdaContext()); + AwsProxyResponse response = LambdaContainerHandler.getObjectMapper().readValue(outputStream.toByteArray(), AwsProxyResponse.class); + + assertEquals(200, response.getStatusCode()); + assertTrue(response.getHeaders().containsKey(CUSTOM_HEADER_KEY)); + assertEquals(CUSTOM_HEADER_VALUE, response.getHeaders().get(CUSTOM_HEADER_KEY)); + assertEquals(BODY_TEXT_RESPONSE, response.getBody()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + private static void configureRoutes() { + get("/hello", (req, res) -> { + res.status(200); + res.header(CUSTOM_HEADER_KEY, CUSTOM_HEADER_VALUE); + return BODY_TEXT_RESPONSE; + }); + + get("/cookie", (req, res) -> { + Cookie testCookie = new Cookie(COOKIE_NAME, COOKIE_VALUE); + testCookie.setDomain(COOKIE_DOMAIN); + testCookie.setPath(COOKIE_PATH); + res.raw().addCookie(testCookie); + return BODY_TEXT_RESPONSE; + }); + + get("/multi-cookie", (req, res) -> { + Cookie testCookie = new Cookie(COOKIE_NAME, COOKIE_VALUE); + testCookie.setDomain(COOKIE_DOMAIN); + testCookie.setPath(COOKIE_PATH); + Cookie testCookie2 = new Cookie(COOKIE_NAME + "2", COOKIE_VALUE + "2"); + testCookie2.setDomain(COOKIE_DOMAIN); + testCookie2.setPath(COOKIE_PATH); + res.raw().addCookie(testCookie); + res.raw().addCookie(testCookie2); + return BODY_TEXT_RESPONSE; + }); + } +} diff --git a/aws-serverless-java-container-spark/src/test/java/com/amazonaws/serverless/proxy/spark/InitExceptionHandlerTest.java b/aws-serverless-java-container-spark/src/test/java/com/amazonaws/serverless/proxy/spark/InitExceptionHandlerTest.java index 1781b212..28f1e808 100644 --- a/aws-serverless-java-container-spark/src/test/java/com/amazonaws/serverless/proxy/spark/InitExceptionHandlerTest.java +++ b/aws-serverless-java-container-spark/src/test/java/com/amazonaws/serverless/proxy/spark/InitExceptionHandlerTest.java @@ -6,6 +6,7 @@ import com.amazonaws.serverless.proxy.AwsProxySecurityContextWriter; 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.spark.embeddedserver.LambdaEmbeddedServer; import com.amazonaws.serverless.proxy.spark.embeddedserver.LambdaEmbeddedServerFactory; @@ -37,11 +38,12 @@ public void initException_mockException_expectHandlerToRun() { when(embeddedServer.ignite(anyString(), anyInt(), anyObject(), anyInt(), anyInt(), anyInt())) .thenThrow(new ContainerInitializationException(TEST_EXCEPTION_MESSAGE, null)); LambdaEmbeddedServerFactory serverFactory = new LambdaEmbeddedServerFactory(embeddedServer); - new SparkLambdaContainerHandler<>(new AwsProxyHttpServletRequestReader(), - new AwsProxyHttpServletResponseWriter(), - new AwsProxySecurityContextWriter(), - new AwsProxyExceptionHandler(), - serverFactory); + new SparkLambdaContainerHandler<>(AwsProxyRequest.class, + new AwsProxyHttpServletRequestReader(), + new AwsProxyHttpServletResponseWriter(), + new AwsProxySecurityContextWriter(), + new AwsProxyExceptionHandler(), + serverFactory); configureRoutes(); Spark.awaitInitialization(); diff --git a/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootLambdaContainerHandler.java b/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootLambdaContainerHandler.java index fd961ee3..6e8edc87 100644 --- a/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootLambdaContainerHandler.java +++ b/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootLambdaContainerHandler.java @@ -66,6 +66,7 @@ public class SpringBootLambdaContainerHandler extends public static SpringBootLambdaContainerHandler getAwsProxyHandler(Class extends WebApplicationInitializer> springBootInitializer) throws ContainerInitializationException { return new SpringBootLambdaContainerHandler<>( + AwsProxyRequest.class, new AwsProxyHttpServletRequestReader(), new AwsProxyHttpServletResponseWriter(), new AwsProxySecurityContextWriter(), @@ -77,6 +78,7 @@ public static SpringBootLambdaContainerHandler requestReader, + public SpringBootLambdaContainerHandler(Class requestTypeClass, + RequestReader requestReader, ResponseWriter responseWriter, SecurityContextWriter securityContextWriter, ExceptionHandler exceptionHandler, Class extends WebApplicationInitializer> springBootInitializer) throws ContainerInitializationException { - super(requestReader, responseWriter, securityContextWriter, exceptionHandler); + super(requestTypeClass, requestReader, responseWriter, securityContextWriter, exceptionHandler); Timer.start("SPRINGBOOT_CONTAINER_HANDLER_CONSTRUCTOR"); this.springBootInitializer = springBootInitializer; Timer.stop("SPRINGBOOT_CONTAINER_HANDLER_CONSTRUCTOR"); 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 108cb384..6d7ef180 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 @@ -55,6 +55,7 @@ public static SpringLambdaContainerHandler ge applicationContext.register(config); return new SpringLambdaContainerHandler<>( + AwsProxyRequest.class, new AwsProxyHttpServletRequestReader(), new AwsProxyHttpServletResponseWriter(), new AwsProxySecurityContextWriter(), @@ -72,6 +73,7 @@ public static SpringLambdaContainerHandler ge public static SpringLambdaContainerHandler getAwsProxyHandler(ConfigurableWebApplicationContext applicationContext) throws ContainerInitializationException { return new SpringLambdaContainerHandler<>( + AwsProxyRequest.class, new AwsProxyHttpServletRequestReader(), new AwsProxyHttpServletResponseWriter(), new AwsProxySecurityContextWriter(), @@ -83,19 +85,21 @@ public static SpringLambdaContainerHandler ge /** * Creates a new container handler with the given reader and writer objects * + * @param requestTypeClass The class for the incoming Lambda event * @param requestReader An implementation of `RequestReader` * @param responseWriter An implementation of `ResponseWriter` * @param securityContextWriter An implementation of `SecurityContextWriter` * @param exceptionHandler An implementation of `ExceptionHandler` * @throws ContainerInitializationException */ - public SpringLambdaContainerHandler(RequestReader requestReader, + public SpringLambdaContainerHandler(Class requestTypeClass, + RequestReader requestReader, ResponseWriter responseWriter, SecurityContextWriter securityContextWriter, ExceptionHandler exceptionHandler, ConfigurableWebApplicationContext applicationContext) throws ContainerInitializationException { - super(requestReader, responseWriter, securityContextWriter, exceptionHandler); + super(requestTypeClass, requestReader, responseWriter, securityContextWriter, exceptionHandler); Timer.start("SPRING_CONTAINER_HANDLER_CONSTRUCTOR"); initializer = new LambdaSpringApplicationInitializer(applicationContext); Timer.stop("SPRING_CONTAINER_HANDLER_CONSTRUCTOR"); 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 34567ce7..860a4157 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 @@ -1,7 +1,6 @@ package com.amazonaws.serverless.sample.jersey; -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; @@ -18,10 +17,10 @@ public class StreamLambdaHandler implements RequestStreamHandler { - private final ResourceConfig jerseyApplication = new ResourceConfig() + private static final ResourceConfig jerseyApplication = new ResourceConfig() .packages("com.amazonaws.serverless.sample.jersey") .register(JacksonFeature.class); - private final JerseyLambdaContainerHandler handler + private static final JerseyLambdaContainerHandler handler = JerseyLambdaContainerHandler.getAwsProxyHandler(jerseyApplication); public StreamLambdaHandler() { @@ -33,13 +32,7 @@ public StreamLambdaHandler() { public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { - AwsProxyRequest request = LambdaContainerHandler.getObjectMapper().readValue(inputStream, AwsProxyRequest.class); - - AwsProxyResponse resp = handler.proxy(request, context); - - LambdaContainerHandler.getObjectMapper().writeValue(outputStream, resp); - - System.err.println(LambdaContainerHandler.getObjectMapper().writeValueAsString(Timer.getTimers())); + handler.proxyStream(inputStream, outputStream, context); // just in case it wasn't closed by the mapper outputStream.close(); diff --git a/samples/spark/pet-store/src/main/java/com/amazonaws/serverless/sample/spark/StreamLambdaHandler.java b/samples/spark/pet-store/src/main/java/com/amazonaws/serverless/sample/spark/StreamLambdaHandler.java index c30c895e..f8a53240 100644 --- a/samples/spark/pet-store/src/main/java/com/amazonaws/serverless/sample/spark/StreamLambdaHandler.java +++ b/samples/spark/pet-store/src/main/java/com/amazonaws/serverless/sample/spark/StreamLambdaHandler.java @@ -2,7 +2,6 @@ import com.amazonaws.serverless.exceptions.ContainerInitializationException; -import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; import com.amazonaws.serverless.proxy.internal.testutils.Timer; import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; @@ -20,7 +19,6 @@ public class StreamLambdaHandler implements RequestStreamHandler { - private boolean isInitialized = false; private SparkLambdaContainerHandler handler; private Logger log = LoggerFactory.getLogger(StreamLambdaHandler.class); @@ -32,8 +30,7 @@ public StreamLambdaHandler() { @Override public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { - if (!isInitialized) { - isInitialized = true; + if (handler == null) { try { handler = SparkLambdaContainerHandler.getAwsProxyHandler(); SparkResources.defineResources(); @@ -44,13 +41,7 @@ public void handleRequest(InputStream inputStream, OutputStream outputStream, Co } } - AwsProxyRequest request = LambdaContainerHandler.getObjectMapper().readValue(inputStream, AwsProxyRequest.class); - - AwsProxyResponse resp = handler.proxy(request, context); - - LambdaContainerHandler.getObjectMapper().writeValue(outputStream, resp); - - System.err.println(LambdaContainerHandler.getObjectMapper().writeValueAsString(Timer.getTimers())); + handler.proxyStream(inputStream, outputStream, context); // just in case it wasn't closed by the mapper outputStream.close(); 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 790fc13c..96a71105 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 @@ -2,7 +2,6 @@ import com.amazonaws.serverless.exceptions.ContainerInitializationException; -import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; import com.amazonaws.serverless.proxy.internal.testutils.Timer; import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; @@ -40,13 +39,7 @@ public void handleRequest(InputStream inputStream, OutputStream outputStream, Co } } - AwsProxyRequest request = LambdaContainerHandler.getObjectMapper().readValue(inputStream, AwsProxyRequest.class); - - AwsProxyResponse resp = handler.proxy(request, context); - - LambdaContainerHandler.getObjectMapper().writeValue(outputStream, resp); - - System.err.println(LambdaContainerHandler.getObjectMapper().writeValueAsString(Timer.getTimers())); + handler.proxyStream(inputStream, outputStream, context); // just in case it wasn't closed by the mapper outputStream.close(); diff --git a/samples/springboot/pet-store/src/main/java/com/amazonaws/serverless/sample/springboot/StreamLambdaHandler.java b/samples/springboot/pet-store/src/main/java/com/amazonaws/serverless/sample/springboot/StreamLambdaHandler.java index abc03895..507a5238 100644 --- a/samples/springboot/pet-store/src/main/java/com/amazonaws/serverless/sample/springboot/StreamLambdaHandler.java +++ b/samples/springboot/pet-store/src/main/java/com/amazonaws/serverless/sample/springboot/StreamLambdaHandler.java @@ -2,7 +2,6 @@ import com.amazonaws.serverless.exceptions.ContainerInitializationException; -import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; import com.amazonaws.serverless.proxy.internal.testutils.Timer; import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; @@ -40,13 +39,7 @@ public void handleRequest(InputStream inputStream, OutputStream outputStream, Co } } - AwsProxyRequest request = LambdaContainerHandler.getObjectMapper().readValue(inputStream, AwsProxyRequest.class); - - AwsProxyResponse resp = handler.proxy(request, context); - - LambdaContainerHandler.getObjectMapper().writeValue(outputStream, resp); - - System.err.println(LambdaContainerHandler.getObjectMapper().writeValueAsString(Timer.getTimers())); + handler.proxyStream(inputStream, outputStream, context); // just in case it wasn't closed by the mapper outputStream.close(); From 2b962c89a15d4e5df54b2677578420100708eac8 Mon Sep 17 00:00:00 2001 From: sapessi Date: Sun, 11 Feb 2018 08:12:18 -0800 Subject: [PATCH 0103/1214] Updated samples to use a static block to declared the handler (#109) and moved them all to the new streamProxy method (#118). --- README.md | 27 ++++++++----------- .../sample/jersey/StreamLambdaHandler.java | 1 - .../sample/spark/StreamLambdaHandler.java | 27 +++++++++---------- .../sample/spring/StreamLambdaHandler.java | 25 +++++++---------- .../springboot/StreamLambdaHandler.java | 25 +++++++---------- 5 files changed, 43 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index b25ff061..752dca60 100644 --- a/README.md +++ b/README.md @@ -13,27 +13,22 @@ Below is the most basic AWS Lambda handler example that launches a Spring applic ```java public class StreamLambdaHandler implements RequestStreamHandler { - private SpringLambdaContainerHandler handler; - private Logger log = LoggerFactory.getLogger(StreamLambdaHandler.class); + private static SpringLambdaContainerHandler handler; + static { + try { + handler = SpringLambdaContainerHandler.getAwsProxyHandler(PetStoreSpringAppConfig.class); + } catch (ContainerInitializationException e) { + // if we fail here. We re-throw the exception to force another cold start + e.printStackTrace(); + throw new RuntimeException("Could not initialize Spring framework", e); + } + } @Override public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { - if (handler == null) { - try { - handler = SpringLambdaContainerHandler.getAwsProxyHandler(PetStoreSpringAppConfig.class); - } catch (ContainerInitializationException e) { - log.error("Cannot initialize Spring container", e); - outputStream.close(); - throw new RuntimeException(e); - } - } - - AwsProxyRequest request = LambdaContainerHandler.getObjectMapper().readValue(inputStream, AwsProxyRequest.class); - - AwsProxyResponse resp = handler.proxy(request, context); + handler.proxyStream(inputStream, outputStream, context); - LambdaContainerHandler.getObjectMapper().writeValue(outputStream, resp); // just in case it wasn't closed by the mapper outputStream.close(); } 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 860a4157..96276958 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 @@ -31,7 +31,6 @@ public StreamLambdaHandler() { @Override public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { - handler.proxyStream(inputStream, outputStream, context); // just in case it wasn't closed by the mapper diff --git a/samples/spark/pet-store/src/main/java/com/amazonaws/serverless/sample/spark/StreamLambdaHandler.java b/samples/spark/pet-store/src/main/java/com/amazonaws/serverless/sample/spark/StreamLambdaHandler.java index f8a53240..a5f2f110 100644 --- a/samples/spark/pet-store/src/main/java/com/amazonaws/serverless/sample/spark/StreamLambdaHandler.java +++ b/samples/spark/pet-store/src/main/java/com/amazonaws/serverless/sample/spark/StreamLambdaHandler.java @@ -9,8 +9,6 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import spark.Spark; import java.io.IOException; @@ -19,8 +17,18 @@ public class StreamLambdaHandler implements RequestStreamHandler { - private SparkLambdaContainerHandler handler; - private Logger log = LoggerFactory.getLogger(StreamLambdaHandler.class); + private static SparkLambdaContainerHandler handler; + static { + try { + handler = SparkLambdaContainerHandler.getAwsProxyHandler(); + SparkResources.defineResources(); + Spark.awaitInitialization(); + } catch (ContainerInitializationException e) { + // if we fail here. We re-throw the exception to force another cold start + e.printStackTrace(); + throw new RuntimeException("Could not initialize Spark container", e); + } + } public StreamLambdaHandler() { // we enable the timer for debugging. This SHOULD NOT be enabled in production. @@ -30,17 +38,6 @@ public StreamLambdaHandler() { @Override public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { - if (handler == null) { - try { - handler = SparkLambdaContainerHandler.getAwsProxyHandler(); - SparkResources.defineResources(); - Spark.awaitInitialization(); - } catch (ContainerInitializationException e) { - log.error("Cannot initialize Spark application", e); - return; - } - } - handler.proxyStream(inputStream, outputStream, context); // just in case it wasn't closed by the mapper 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 96a71105..11fddada 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 @@ -9,17 +9,22 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class StreamLambdaHandler implements RequestStreamHandler { - private SpringLambdaContainerHandler handler; - private Logger log = LoggerFactory.getLogger(StreamLambdaHandler.class); + private static SpringLambdaContainerHandler handler; + static { + try { + handler = SpringLambdaContainerHandler.getAwsProxyHandler(PetStoreSpringAppConfig.class); + } catch (ContainerInitializationException e) { + // if we fail here. We re-throw the exception to force another cold start + e.printStackTrace(); + throw new RuntimeException("Could not initialize Spring framework", e); + } + } public StreamLambdaHandler() { // we enable the timer for debugging. This SHOULD NOT be enabled in production. @@ -29,16 +34,6 @@ public StreamLambdaHandler() { @Override public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { - if (handler == null) { - try { - handler = SpringLambdaContainerHandler.getAwsProxyHandler(PetStoreSpringAppConfig.class); - } catch (ContainerInitializationException e) { - log.error("Cannot initialize Spring container", e); - outputStream.close(); - throw new RuntimeException(e); - } - } - handler.proxyStream(inputStream, outputStream, context); // just in case it wasn't closed by the mapper diff --git a/samples/springboot/pet-store/src/main/java/com/amazonaws/serverless/sample/springboot/StreamLambdaHandler.java b/samples/springboot/pet-store/src/main/java/com/amazonaws/serverless/sample/springboot/StreamLambdaHandler.java index 507a5238..1fc50549 100644 --- a/samples/springboot/pet-store/src/main/java/com/amazonaws/serverless/sample/springboot/StreamLambdaHandler.java +++ b/samples/springboot/pet-store/src/main/java/com/amazonaws/serverless/sample/springboot/StreamLambdaHandler.java @@ -9,17 +9,22 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class StreamLambdaHandler implements RequestStreamHandler { - private SpringBootLambdaContainerHandler handler; - private Logger log = LoggerFactory.getLogger(StreamLambdaHandler.class); + private static SpringBootLambdaContainerHandler handler; + static { + try { + handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class); + } catch (ContainerInitializationException e) { + // if we fail here. We re-throw the exception to force another cold start + e.printStackTrace(); + throw new RuntimeException("Could not initialize Spring Boot application", e); + } + } public StreamLambdaHandler() { // we enable the timer for debugging. This SHOULD NOT be enabled in production. @@ -29,16 +34,6 @@ public StreamLambdaHandler() { @Override public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { - if (handler == null) { - try { - handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class); - } catch (ContainerInitializationException e) { - log.error("Cannot initialize Spring container", e); - outputStream.close(); - throw new RuntimeException(e); - } - } - handler.proxyStream(inputStream, outputStream, context); // just in case it wasn't closed by the mapper From 498e98985a1f2cb9f782cdb53d96f7f26e896987 Mon Sep 17 00:00:00 2001 From: sapessi Date: Sun, 11 Feb 2018 08:30:41 -0800 Subject: [PATCH 0104/1214] Added unit test for Jersey principal injection. --- .../internal/testutils/AwsProxyRequestBuilder.java | 4 ++++ .../proxy/jersey/EchoJerseyResource.java | 14 ++++++++++++++ .../proxy/jersey/JerseyAwsProxyTest.java | 11 +++++++++++ 3 files changed, 29 insertions(+) diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java index 6b88074c..60a028aa 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java @@ -168,6 +168,10 @@ public AwsProxyRequestBuilder authorizerPrincipal(String principal) { 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); return this; } 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 86bac4f3..d3681b91 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 @@ -25,6 +25,7 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriInfo; import java.util.Enumeration; import java.util.Random; @@ -37,6 +38,9 @@ public class EchoJerseyResource { public static final String SERVLET_RESP_HEADER_KEY = "X-HttpServletResponse"; public static final String EXCEPTION_MESSAGE = "Fake exception"; + @Context + SecurityContext securityCtx; + @Path("/headers") @GET @Produces(MediaType.APPLICATION_JSON) public MapResponseModel echoHeaders(@Context ContainerRequestContext context) { @@ -48,6 +52,16 @@ public MapResponseModel echoHeaders(@Context ContainerRequestContext context) { return headers; } + @Path("/security-context") @GET + @Produces(MediaType.APPLICATION_JSON) + public SingleValueModel getPrincipal() { + SingleValueModel output = new SingleValueModel(); + if (securityCtx != null) { + output.setValue(securityCtx.getUserPrincipal().getName()); + } + return output; + } + @Path("/servlet-headers") @GET @Produces(MediaType.APPLICATION_JSON) public MapResponseModel echoServletHeaders(@Context HttpServletRequest context) { 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 055d6313..6945a102 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 @@ -54,6 +54,7 @@ public class JerseyAwsProxyTest { private static final String QUERY_STRING_KEY = "identifier"; private static final String QUERY_STRING_NON_ENCODED_VALUE = "Space Test"; private static final String QUERY_STRING_ENCODED_VALUE = "Space%20Test"; + private static final String USER_PRINCIPAL = "user1"; private static ObjectMapper objectMapper = new ObjectMapper(); @@ -302,6 +303,16 @@ public void stripBasePath_route_shouldReturn404() { handler.stripBasePath(""); } + @Test + public void securityContext_injectPrincipal_expectPrincipalName() { + AwsProxyRequest request = new AwsProxyRequestBuilder("/echo/security-context", "GET") + .authorizerPrincipal(USER_PRINCIPAL).build(); + + AwsProxyResponse resp = handler.proxy(request, lambdaContext); + assertEquals(200, resp.getStatusCode()); + validateSingleValueModel(resp, USER_PRINCIPAL); + } + private void validateMapResponseModel(AwsProxyResponse output) { validateMapResponseModel(output, CUSTOM_HEADER_KEY, CUSTOM_HEADER_VALUE); } From 7dbb3f2a015dee17923ec58ec9e0d490135d9b8b Mon Sep 17 00:00:00 2001 From: sapessi Date: Tue, 13 Feb 2018 11:17:53 -0800 Subject: [PATCH 0105/1214] Performance improvements all around. This addresses #99. --- .../internal/LambdaContainerHandler.java | 24 +++++++--- .../ApacheCombinedServletLogFormatter.java | 42 +++++++++++++---- .../servlet/AwsHttpServletRequest.java | 7 ++- .../AwsLambdaServletContainerHandler.java | 3 +- .../servlet/AwsProxyHttpServletRequest.java | 47 ++++++++++--------- .../AwsProxyHttpServletRequestFormTest.java | 16 ------- .../proxy/jersey/JerseyHandlerFilter.java | 39 +++++++++++---- .../jersey/JerseyLambdaContainerHandler.java | 17 +++---- .../spark/SparkLambdaContainerHandler.java | 4 +- .../proxy/spark/InitExceptionHandlerTest.java | 2 + .../SpringBootLambdaContainerHandler.java | 4 +- .../spring/SpringLambdaContainerHandler.java | 5 +- 12 files changed, 132 insertions(+), 78 deletions(-) diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandler.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandler.java index 9e268d2f..23c37e6e 100644 --- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandler.java +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandler.java @@ -25,6 +25,8 @@ import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.databind.ObjectWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,20 +64,22 @@ public abstract class LambdaContainerHandler securityContextWriter; private ExceptionHandler exceptionHandler; private Class requestTypeClass; + private Class responseTypeClass; protected Context lambdaContext; private LogFormatter logFormatter; private Logger log = LoggerFactory.getLogger(LambdaContainerHandler.class); - + private ObjectReader objectReader; + private ObjectWriter objectWriter; //------------------------------------------------------------- // Variables - Private - Static //------------------------------------------------------------- private static ContainerConfig config = ContainerConfig.defaultConfig(); - private static volatile ObjectMapper objectMapper; + private static ObjectMapper objectMapper = new ObjectMapper(); @@ -84,12 +88,14 @@ public abstract class LambdaContainerHandler requestClass, + Class responseClass, RequestReader requestReader, ResponseWriter responseWriter, SecurityContextWriter securityContextWriter, ExceptionHandler exceptionHandler) { log.info("Starting Lambda Container Handler"); requestTypeClass = requestClass; + responseTypeClass = responseClass; this.requestReader = requestReader; this.responseWriter = responseWriter; this.securityContextWriter = securityContextWriter; @@ -113,9 +119,6 @@ protected abstract void handleRequest(ContainerRequestType containerRequest, Con //------------------------------------------------------------- public static ObjectMapper getObjectMapper() { - if (objectMapper == null) { - objectMapper = new ObjectMapper(); - } return objectMapper; } @@ -188,10 +191,17 @@ public void proxyStream(InputStream input, OutputStream output, Context context) throws IOException { try { - RequestType request = getObjectMapper().readValue(input, requestTypeClass); + if (objectReader == null) { + objectReader = getObjectMapper().readerFor(requestTypeClass); + } + RequestType request = objectReader.readValue(input); ResponseType resp = proxy(request, context); - getObjectMapper().writeValue(output, resp); + if (objectWriter == null) { + objectWriter = getObjectMapper().writerFor(responseTypeClass); + } + + objectWriter.writeValue(output, resp); } catch (JsonParseException e) { log.error("Error while parsing request object stream", e); getObjectMapper().writeValue(output, exceptionHandler.handle(e)); 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 66b8fc1b..37895dd3 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 @@ -10,13 +10,19 @@ import javax.servlet.http.HttpServletResponse; import javax.ws.rs.core.SecurityContext; -import java.text.SimpleDateFormat; -import java.time.Instant; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; import java.util.Locale; import static com.amazonaws.serverless.proxy.RequestReader.API_GATEWAY_CONTEXT_PROPERTY; +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; +import static java.time.temporal.ChronoField.YEAR; /** @@ -27,10 +33,30 @@ */ public class ApacheCombinedServletLogFormatter