From b49b932ffc867b901cea131d8e1090e4f8235a14 Mon Sep 17 00:00:00 2001 From: sapessi Date: Mon, 19 Jun 2017 18:17:42 -0700 Subject: [PATCH 1/3] Addresses issues #35 and #37 * Basic implementation of RequestDispatcher for AwsProxyRequest type * More unit tests for multipart uploads and filters * Servlet Context as well as request injection for Jersey --- aws-serverless-java-container-core/pom.xml | 9 ++ .../internal/LambdaContainerHandler.java | 8 +- .../servlet/AwsHttpServletRequest.java | 30 ++++++- .../servlet/AwsHttpServletResponse.java | 1 + .../AwsLambdaServletContainerHandler.java | 88 +++++++++++++++---- .../servlet/AwsProxyHttpServletRequest.java | 11 ++- .../AwsProxyHttpServletRequestReader.java | 4 + .../servlet/AwsProxyRequestDispatcher.java | 88 +++++++++++++++++++ .../internal/servlet/AwsServletContext.java | 31 +++---- .../servlet/AwsFilterChainManagerTest.java | 13 +-- .../AwsProxyHttpServletRequestFormTest.java | 69 +++++++++++++++ .../AwsProxyHttpServletRequestTest.java | 8 +- .../internal/servlet/FilterHolderTest.java | 2 +- .../AwsProxyServletRequestFactory.java | 5 +- .../spring/SpringLambdaContainerHandler.java | 13 +-- .../spring/SpringServletContextTest.java | 3 + .../proxy/spring/StaticAppProxyTest.java | 32 +++++++ .../proxy/spring/echoapp/EchoResource.java | 2 +- .../spring/echoapp/EchoSpringAppConfig.java | 3 + .../proxy/spring/staticapp/LambdaHandler.java | 37 ++++++++ .../proxy/spring/staticapp/MyController.java | 16 ++++ .../src/test/resources/staticAppContext.xml | 39 ++++++++ .../src/test/resources/templates/sample.html | 5 ++ samples/jersey/pet-store/pom.xml | 2 +- samples/spark/pet-store/pom.xml | 2 +- samples/spring/pet-store/pom.xml | 2 +- .../sample/spring/LambdaHandler.java | 4 +- .../sample/spring/PetsController.java | 4 +- .../sample/spring/StreamLambdaHandler.java | 3 - 29 files changed, 464 insertions(+), 70 deletions(-) create mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyRequestDispatcher.java create mode 100644 aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestFormTest.java create mode 100644 aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/StaticAppProxyTest.java create mode 100755 aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/staticapp/LambdaHandler.java create mode 100755 aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/staticapp/MyController.java create mode 100755 aws-serverless-java-container-spring/src/test/resources/staticAppContext.xml create mode 100755 aws-serverless-java-container-spring/src/test/resources/templates/sample.html diff --git a/aws-serverless-java-container-core/pom.xml b/aws-serverless-java-container-core/pom.xml index a2d09710e..d5de78425 100644 --- a/aws-serverless-java-container-core/pom.xml +++ b/aws-serverless-java-container-core/pom.xml @@ -65,6 +65,15 @@ 1.10.19 test + + + + org.apache.httpcomponents + httpmime + 4.5.3 + test + + 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 bff64b9af..aed34c08d 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 @@ -46,6 +46,9 @@ public abstract class LambdaContainerHandler exceptionHandler; + protected Context lambdaContext; + + //------------------------------------------------------------- // Constructors //------------------------------------------------------------- @@ -85,6 +88,7 @@ protected abstract void handleRequest(ContainerRequestType containerRequest, Con * @return A valid response type */ public ResponseType proxy(RequestType request, Context context) { + lambdaContext = context; try { SecurityContext securityContext = securityContextWriter.writeSecurityContext(request, context); CountDownLatch latch = new CountDownLatch(1); @@ -98,10 +102,6 @@ public ResponseType proxy(RequestType request, Context context) { return responseWriter.writeResponse(containerResponse, context); } catch (Exception e) { context.getLogger().log("Error while handling request: " + e.getMessage()); - - /*for (StackTraceElement el : e.getStackTrace()) { - context.getLogger().log(el.toString()); - }*/ e.printStackTrace(); return exceptionHandler.handle(e); 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 53106fd89..221c64832 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 @@ -23,7 +23,13 @@ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.stream.Collectors; @@ -56,6 +62,9 @@ public abstract class AwsHttpServletRequest implements HttpServletRequest { private Context lambdaContext; private Map attributes; + private ServletContext servletContext; + + protected DispatcherType dispatcherType; //------------------------------------------------------------- @@ -72,6 +81,7 @@ public abstract class AwsHttpServletRequest implements HttpServletRequest { attributes = new HashMap<>(); } + //------------------------------------------------------------- // Implementation - HttpServletRequest //------------------------------------------------------------- @@ -119,6 +129,7 @@ public boolean isRequestedSessionIdFromURL() { @Override + @Deprecated public boolean isRequestedSessionIdFromUrl() { return false; } @@ -185,7 +196,7 @@ public int getLocalPort() { @Override public ServletContext getServletContext() { - return AwsServletContext.getInstance(lambdaContext); + return servletContext; } @@ -213,6 +224,19 @@ public DispatcherType getDispatcherType() { } + //------------------------------------------------------------- + // Methods - Getter/Setter + //------------------------------------------------------------- + + public void setDispatcherType(DispatcherType type) { + dispatcherType = type; + } + + public void setServletContext(ServletContext context) { + servletContext = context; + } + + //------------------------------------------------------------- // Methods - Protected //------------------------------------------------------------- @@ -223,7 +247,6 @@ public DispatcherType getDispatcherType() { * @return An array of Cookie objects from the header */ protected Cookie[] parseCookieHeaderValue(String headerValue) { - List> parsedHeaders = this.parseHeaderValue(headerValue); return parsedHeaders.stream() @@ -232,6 +255,7 @@ protected Cookie[] parseCookieHeaderValue(String headerValue) { .toArray(Cookie[]::new); } + /** * 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. 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 871f7975e..cbe5e2cb1 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 @@ -188,6 +188,7 @@ public void setStatus(int i) { @Override + @Deprecated public void setStatus(int i, String s) { statusCode = i; statusMessage = s; 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 82d73b200..75db82f5f 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 @@ -17,9 +17,12 @@ import com.amazonaws.serverless.proxy.internal.RequestReader; import com.amazonaws.serverless.proxy.internal.ResponseWriter; import com.amazonaws.serverless.proxy.internal.SecurityContextWriter; +import com.amazonaws.services.lambda.runtime.Context; import javax.servlet.ServletContext; import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @@ -28,6 +31,8 @@ * Abstract extension of the code 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 * @param * @param @@ -42,14 +47,13 @@ public abstract class AwsLambdaServletContainerHandler filterChainManager; //------------------------------------------------------------- @@ -63,30 +67,43 @@ protected AwsLambdaServletContainerHandler(RequestReaderAwsProxyRequestDispatcher object + * @param servletRequest The modified request object with the new request path + * @param servletResponse The original servlet response + * @throws ServletException + * @throws IOException */ - public ServletContext getServletContext() { - return servletContext; + public void forward(ContainerRequestType servletRequest, ContainerResponseType servletResponse) + throws ServletException, IOException { + try { + handleRequest(servletRequest, servletResponse, lambdaContext); + } catch (Exception e) { + e.printStackTrace(); + throw new ServletException(e); + } } /** - * Sets the ServletContext in the handler and initialized a new 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 c11111a7e..22658f3df 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 Map getMultipartFormParametersMap() { Map output = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - ServletFileUpload upload = new ServletFileUpload(); + ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory()); try { List items = upload.parseRequest(this); for (FileItem item : items) { 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 3358e2705..e5e381b22 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 @@ -36,9 +36,13 @@ public AwsProxyHttpServletRequest readRequest(AwsProxyRequest request, SecurityC servletRequest.setAttribute(API_GATEWAY_CONTEXT_PROPERTY, request.getRequestContext()); servletRequest.setAttribute(API_GATEWAY_STAGE_VARS_PROPERTY, request.getStageVariables()); servletRequest.setAttribute(LAMBDA_CONTEXT_PROPERTY, lambdaContext); + return servletRequest; } + //------------------------------------------------------------- + // Methods - Protected + //------------------------------------------------------------- @Override protected Class getRequestClass() { 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 new file mode 100644 index 000000000..7e4b2299e --- /dev/null +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyRequestDispatcher.java @@ -0,0 +1,88 @@ +package com.amazonaws.serverless.proxy.internal.servlet; + + +import com.amazonaws.serverless.proxy.internal.model.AwsProxyRequest; +import com.amazonaws.serverless.proxy.internal.model.AwsProxyResponse; + +import javax.servlet.DispatcherType; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import java.io.IOException; + + +/** + * Default RequestDispatcher 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 fdadce13a..76c4b2eb3 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 Map attributes; private Map initParameters; + private AwsLambdaServletContainerHandler containerHandler; //------------------------------------------------------------- @@ -79,9 +80,9 @@ public class AwsServletContext // Constructors //------------------------------------------------------------- - private AwsServletContext(Context lambdaContext) { + public AwsServletContext(Context lambdaContext, AwsLambdaServletContainerHandler containerHandler) { this.lambdaContext = lambdaContext; - + this.containerHandler = containerHandler; this.attributes = new HashMap<>(); this.initParameters = new HashMap<>(); this.filters = new LinkedHashMap<>(); @@ -92,16 +93,6 @@ private AwsServletContext(Context lambdaContext) { // Implementation - ServletContext //------------------------------------------------------------- - - public static ServletContext getInstance(Context lambdaContext) { - if (instance == null) { - instance = new AwsServletContext(lambdaContext); - } - - return instance; - } - - public static void clearServletContextCache() { instance = null; } @@ -178,33 +169,34 @@ public InputStream getResourceAsStream(String s) { @Override public RequestDispatcher getRequestDispatcher(String s) { - // TODO: This should be part of the reader interface described in the getResourcePaths method - return null; + return new AwsProxyRequestDispatcher(s, containerHandler); } @Override public RequestDispatcher getNamedDispatcher(String s) { - // TODO: This should be part of the reader interface described in the getResourcePaths method - return null; + throw new UnsupportedOperationException(); } @Override + @Deprecated public Servlet getServlet(String s) throws ServletException { - return null; + throw new UnsupportedOperationException(); } @Override + @Deprecated public Enumeration getServlets() { - return null; + throw new UnsupportedOperationException(); } @Override + @Deprecated public Enumeration getServletNames() { - return null; + throw new UnsupportedOperationException(); } @@ -215,6 +207,7 @@ public void log(String s) { @Override + @Deprecated public void log(Exception e, String s) { lambdaContext.getLogger().log(s); lambdaContext.getLogger().log(e.getMessage()); 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 e0dad89a8..102ba8336 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 @@ -17,19 +17,20 @@ public class AwsFilterChainManagerTest { private static AwsFilterChainManager chainManager; private static Context lambdaContext = new MockLambdaContext(); + private static ServletContext servletContext; @BeforeClass public static void setUp() { - ServletContext context = AwsServletContext.getInstance(lambdaContext); + servletContext = new AwsServletContext(lambdaContext, null);//AwsServletContext.getInstance(lambdaContext, null); - FilterRegistration.Dynamic reg = context.addFilter("Filter1", new MockFilter()); + FilterRegistration.Dynamic reg = servletContext.addFilter("Filter1", new MockFilter()); reg.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/first/second"); - FilterRegistration.Dynamic reg2 = context.addFilter("Filter2", new MockFilter()); + FilterRegistration.Dynamic reg2 = servletContext.addFilter("Filter2", new MockFilter()); reg2.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/second/*"); - FilterRegistration.Dynamic reg3 = context.addFilter("Filter3", new MockFilter()); + FilterRegistration.Dynamic reg3 = servletContext.addFilter("Filter3", new MockFilter()); reg3.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/third/fourth/*"); - chainManager = new AwsFilterChainManager((AwsServletContext) context); + chainManager = new AwsFilterChainManager((AwsServletContext) servletContext); } @Test @@ -106,6 +107,7 @@ public void filterChain_getFilterChain_subsetOfFilters() { 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()); @@ -130,6 +132,7 @@ public void filterChain_getFilterChain_multipleFilters() { AwsProxyHttpServletRequest req = new AwsProxyHttpServletRequest( new AwsProxyRequestBuilder("/second/important", "GET").build(), lambdaContext, null ); + req.setServletContext(servletContext); FilterRegistration.Dynamic reg = req.getServletContext().addFilter("Filter4", new MockFilter()); reg.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/second/*"); FilterChainHolder fcHolder = chainManager.getFilterChain(req); 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 new file mode 100644 index 000000000..8186de538 --- /dev/null +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestFormTest.java @@ -0,0 +1,69 @@ +package com.amazonaws.serverless.proxy.internal.servlet; + + +import com.amazonaws.serverless.proxy.internal.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 java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + + +public class AwsProxyHttpServletRequestFormTest { + private static final String PART_KEY_1 = "test1"; + private static final String PART_VALUE_1 = "value1"; + private static final String PART_KEY_2 = "test2"; + private static final String PART_VALUE_2 = "value2"; + + private static final HttpEntity SIMPLE_FORM_DATA = MultipartEntityBuilder.create() + .addTextBody(PART_KEY_1, PART_VALUE_1) + .addTextBody(PART_KEY_2, PART_VALUE_2) + .build(); + @Test + public void postForm_getParts_parsing() { + try { + AwsProxyRequest proxyRequest = new AwsProxyRequestBuilder("/form", "POST") + .header(SIMPLE_FORM_DATA.getContentType().getName(), SIMPLE_FORM_DATA.getContentType().getValue()) + //.header(formData.getContentEncoding().getName(), formData.getContentEncoding().getValue()) + .body(IOUtils.toString(SIMPLE_FORM_DATA.getContent())) + .build(); + + HttpServletRequest request = new AwsProxyHttpServletRequest(proxyRequest, null, null); + assertNotNull(request.getParts()); + assertEquals(2, request.getParts().size()); + assertEquals(PART_VALUE_1, IOUtils.toString(request.getPart(PART_KEY_1).getInputStream())); + assertEquals(PART_VALUE_2, IOUtils.toString(request.getPart(PART_KEY_2).getInputStream())); + } catch (IOException | ServletException e) { + fail(e.getMessage()); + } + } + + @Test + public void getForm_getParts_exception() { + try { + AwsProxyRequest proxyRequest = new AwsProxyRequestBuilder("/form", "GET") + .header(SIMPLE_FORM_DATA.getContentType().getName(), SIMPLE_FORM_DATA.getContentType().getValue()) + //.header(formData.getContentEncoding().getName(), formData.getContentEncoding().getValue()) + .body(IOUtils.toString(SIMPLE_FORM_DATA.getContent())) + .build(); + + HttpServletRequest request = new AwsProxyHttpServletRequest(proxyRequest, null, null); + assertNotNull(request.getParts()); + assertEquals(0, request.getParts().size()); + } catch (IOException | ServletException e) { + fail(e.getMessage()); + } + } + + // TODO: Multipart tests +} 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 d9ddc313b..c7884a6e2 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 @@ -2,12 +2,18 @@ import com.amazonaws.serverless.proxy.internal.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; @@ -170,7 +176,7 @@ public void queryParameters_getParameterNames_null() { } @Test - public void queryParameters_getParameterNames_nonNull() { + public void queryParameters_getParameterNames_notNull() { HttpServletRequest request = new AwsProxyHttpServletRequest(REQUEST_QUERY, null, null); List parameterNames = Collections.list(request.getParameterNames()); assertNotNull(request); diff --git a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/FilterHolderTest.java b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/FilterHolderTest.java index 31461d065..2403207a8 100644 --- a/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/FilterHolderTest.java +++ b/aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/FilterHolderTest.java @@ -12,7 +12,7 @@ public class FilterHolderTest { @Test public void annotation_filterRegistration_pathValidator() { - FilterHolder holder = new FilterHolder(new UrlPathValidator(), AwsServletContext.getInstance(lambdaContext)); + FilterHolder holder = new FilterHolder(new UrlPathValidator(), new AwsServletContext(lambdaContext, null)); assertTrue(holder.isAnnotated()); assertNotEquals(UrlPathValidator.class.getName(), holder.getRegistration().getName()); diff --git a/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/factory/AwsProxyServletRequestFactory.java b/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/factory/AwsProxyServletRequestFactory.java index 667d5326c..d44ed313e 100644 --- a/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/factory/AwsProxyServletRequestFactory.java +++ b/aws-serverless-java-container-jersey/src/main/java/com/amazonaws/serverless/proxy/jersey/factory/AwsProxyServletRequestFactory.java @@ -17,6 +17,7 @@ import com.amazonaws.serverless.proxy.internal.AwsProxySecurityContextWriter; import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletRequest; import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletRequestReader; +import com.amazonaws.serverless.proxy.internal.servlet.AwsServletContext; import com.amazonaws.serverless.proxy.jersey.JerseyAwsProxyRequestReader; import org.glassfish.hk2.api.Factory; @@ -62,9 +63,11 @@ public void dispose(HttpServletRequest httpServletRequest) { public static HttpServletRequest getRequest() { try { - return requestReader.readRequest(JerseyAwsProxyRequestReader.getCurrentRequest(), + AwsProxyHttpServletRequest req = requestReader.readRequest(JerseyAwsProxyRequestReader.getCurrentRequest(), AwsProxySecurityContextWriter.getCurrentContext(), JerseyAwsProxyRequestReader.getCurrentLambdaContext()); + req.setServletContext(new AwsServletContext(JerseyAwsProxyRequestReader.getCurrentLambdaContext(), null)); + return req; } catch (InvalidRequestEventException e) { e.printStackTrace(); return null; 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 3a911d185..faacbd463 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 @@ -117,21 +117,24 @@ protected void handleRequest(AwsProxyHttpServletRequest containerRequest, AwsHtt throw new ContainerInitializationException(LambdaSpringApplicationInitializer.ERROR_NO_CONTEXT, null); } + // this method of the AwsLambdaServletContainerHandler sets the request context + super.handleRequest(containerRequest, containerResponse, lambdaContext); + // wire up the application context on the first invocation if (!initialized) { - // The servlet context should not be linked to a specific request object, only to the Lambda - // context so we only set it once. - setServletContext(containerRequest.getServletContext()); - initializer.onStartup(this.servletContext); + + initializer.onStartup(getServletContext()); // call the onStartup event if set to give developers a chance to set filters in the context if (startupHandler != null) { - startupHandler.onStartup(this.servletContext); + startupHandler.onStartup(getServletContext()); } initialized = true; } + containerRequest.setServletContext(getServletContext()); + // process filters doFilter(containerRequest, containerResponse); // invoke servlet diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringServletContextTest.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringServletContextTest.java index 942e2062c..075db5bb7 100644 --- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringServletContextTest.java +++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringServletContextTest.java @@ -12,10 +12,13 @@ import com.amazonaws.serverless.proxy.spring.echoapp.model.ValidatedUserModel; import org.junit.BeforeClass; import org.junit.Test; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import javax.servlet.DispatcherType; import javax.servlet.FilterRegistration; +import javax.ws.rs.core.MediaType; + import java.util.EnumSet; import static org.junit.Assert.*; diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/StaticAppProxyTest.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/StaticAppProxyTest.java new file mode 100644 index 000000000..37cde971b --- /dev/null +++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/StaticAppProxyTest.java @@ -0,0 +1,32 @@ +package com.amazonaws.serverless.proxy.spring; + + +import com.amazonaws.serverless.proxy.internal.model.AwsProxyRequest; +import com.amazonaws.serverless.proxy.internal.model.AwsProxyResponse; +import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; +import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext; +import com.amazonaws.serverless.proxy.spring.staticapp.LambdaHandler; + +import org.junit.Test; +import org.springframework.http.HttpHeaders; + +import javax.ws.rs.core.MediaType; + +import static org.junit.Assert.*; + + +public class StaticAppProxyTest { + + private LambdaHandler lambdaHandler = new LambdaHandler(); + + @Test + public void staticPage() { + AwsProxyRequest req = new AwsProxyRequestBuilder("/sample/page", "GET").build(); + AwsProxyResponse resp = lambdaHandler.handleRequest(req, new MockLambdaContext()); + + assertEquals(200, resp.getStatusCode()); + assertTrue(resp.getBody().startsWith("")); + assertTrue(resp.getHeaders().containsKey(HttpHeaders.CONTENT_TYPE)); + assertEquals(MediaType.TEXT_HTML, resp.getHeaders().get(HttpHeaders.CONTENT_TYPE)); + } +} diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/echoapp/EchoResource.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/echoapp/EchoResource.java index 486d67c2d..553116cba 100644 --- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/echoapp/EchoResource.java +++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/echoapp/EchoResource.java @@ -8,7 +8,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import org.springframework.web.context.ServletContextAware; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import javax.servlet.ServletContext; @@ -110,4 +109,5 @@ public SingleValueModel echoRequestURL(HttpServletRequest request) { return valueModel; } + } diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/echoapp/EchoSpringAppConfig.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/echoapp/EchoSpringAppConfig.java index 230ec4bda..93134c1bf 100644 --- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/echoapp/EchoSpringAppConfig.java +++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/echoapp/EchoSpringAppConfig.java @@ -5,6 +5,7 @@ import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @@ -12,6 +13,7 @@ import org.springframework.context.annotation.PropertySource; import org.springframework.web.context.ConfigurableWebApplicationContext; + @Configuration @ComponentScan("com.amazonaws.serverless.proxy.spring.echoapp") @PropertySource("classpath:application.properties") @@ -24,6 +26,7 @@ public class EchoSpringAppConfig { public SpringLambdaContainerHandler springLambdaContainerHandler() throws ContainerInitializationException { SpringLambdaContainerHandler handler = SpringLambdaContainerHandler.getAwsProxyHandler(applicationContext); handler.setRefreshContext(false); + return handler; } diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/staticapp/LambdaHandler.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/staticapp/LambdaHandler.java new file mode 100755 index 000000000..101c63faf --- /dev/null +++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/staticapp/LambdaHandler.java @@ -0,0 +1,37 @@ + package com.amazonaws.serverless.proxy.spring.staticapp; + + + import com.amazonaws.serverless.exceptions.ContainerInitializationException; + import com.amazonaws.serverless.proxy.internal.model.AwsProxyRequest; + import com.amazonaws.serverless.proxy.internal.model.AwsProxyResponse; + import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler; + 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 + { + SpringLambdaContainerHandler handler; + boolean isinitialized = false; + + public AwsProxyResponse handleRequest(AwsProxyRequest awsProxyRequest, Context context) + { + if (!isinitialized) { + isinitialized = true; + try { + XmlWebApplicationContext wc = new XmlWebApplicationContext(); + wc.setConfigLocation("classpath:/staticAppContext.xml"); + handler = SpringLambdaContainerHandler.getAwsProxyHandler(wc); + } 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/staticapp/MyController.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/staticapp/MyController.java new file mode 100755 index 000000000..d4dffc37d --- /dev/null +++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/staticapp/MyController.java @@ -0,0 +1,16 @@ +package com.amazonaws.serverless.proxy.spring.staticapp; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + + +@EnableWebMvc +@Controller +public class MyController { + + @RequestMapping({ "/sample/page" }) + public String showPage() { + return "sample"; + } +} diff --git a/aws-serverless-java-container-spring/src/test/resources/staticAppContext.xml b/aws-serverless-java-container-spring/src/test/resources/staticAppContext.xml new file mode 100755 index 000000000..712f48c02 --- /dev/null +++ b/aws-serverless-java-container-spring/src/test/resources/staticAppContext.xml @@ -0,0 +1,39 @@ + + + + + + + classpath:application.properties + + + + + + + + + + + /templates/ + + + .html + + + + + + + + + \ No newline at end of file diff --git a/aws-serverless-java-container-spring/src/test/resources/templates/sample.html b/aws-serverless-java-container-spring/src/test/resources/templates/sample.html new file mode 100755 index 000000000..cec03da04 --- /dev/null +++ b/aws-serverless-java-container-spring/src/test/resources/templates/sample.html @@ -0,0 +1,5 @@ + + +hello world + + \ No newline at end of file diff --git a/samples/jersey/pet-store/pom.xml b/samples/jersey/pet-store/pom.xml index f380e7bcb..1080e3ca6 100644 --- a/samples/jersey/pet-store/pom.xml +++ b/samples/jersey/pet-store/pom.xml @@ -34,7 +34,7 @@ com.amazonaws.serverless aws-serverless-java-container-jersey - LATEST + [0.1,) diff --git a/samples/spark/pet-store/pom.xml b/samples/spark/pet-store/pom.xml index 25a912870..4d78fb9ca 100644 --- a/samples/spark/pet-store/pom.xml +++ b/samples/spark/pet-store/pom.xml @@ -34,7 +34,7 @@ com.amazonaws.serverless aws-serverless-java-container-spark - LATEST + [0.1,) diff --git a/samples/spring/pet-store/pom.xml b/samples/spring/pet-store/pom.xml index b12533028..5f6db9310 100644 --- a/samples/spring/pet-store/pom.xml +++ b/samples/spring/pet-store/pom.xml @@ -34,7 +34,7 @@ com.amazonaws.serverless aws-serverless-java-container-spring - LATEST + [0.1,) diff --git a/samples/spring/pet-store/src/main/java/com/amazonaws/serverless/sample/spring/LambdaHandler.java b/samples/spring/pet-store/src/main/java/com/amazonaws/serverless/sample/spring/LambdaHandler.java index f5cf44739..af0545fe1 100644 --- a/samples/spring/pet-store/src/main/java/com/amazonaws/serverless/sample/spring/LambdaHandler.java +++ b/samples/spring/pet-store/src/main/java/com/amazonaws/serverless/sample/spring/LambdaHandler.java @@ -19,9 +19,7 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; -/** - * Created by bulianis on 12/13/16. - */ + public class LambdaHandler implements RequestHandler { private SpringLambdaContainerHandler handler; 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 9d866041d..df622b10d 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 @@ -17,6 +17,7 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import java.security.Principal; import java.util.Optional; import java.util.UUID; @@ -35,7 +36,8 @@ public Pet createPet(@RequestBody Pet newPet) { } @RequestMapping(path = "/pets", method = RequestMethod.GET) - public Pet[] listPets(@RequestParam("limit") Optional limit) { + public Pet[] listPets(@RequestParam("limit") Optional limit, Principal principal) { + System.out.println(principal.getName()); int queryLimit = 10; if (limit.isPresent()) { queryLimit = limit.get(); 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 c4bafd058..29647d9b7 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 @@ -15,9 +15,6 @@ import java.io.OutputStream; -/** - * Created by bulianis on 5/2/17. - */ public class StreamLambdaHandler implements RequestStreamHandler { private SpringLambdaContainerHandler handler; private static ObjectMapper mapper = new ObjectMapper(); From 0c7e579d00f69ea27c3cf19d5189741481631dc4 Mon Sep 17 00:00:00 2001 From: sapessi Date: Tue, 20 Jun 2017 11:29:25 -0700 Subject: [PATCH 2/3] Added more unit tests and addressed issue #34 Added the `stripBasePath(String)` method to the LambdaContainerHandler object. --- .../internal/LambdaContainerHandler.java | 11 +++- .../proxy/internal/RequestReader.java | 30 +++++++++- .../proxy/internal/model/ContainerConfig.java | 56 +++++++++++++++++++ .../servlet/AwsProxyHttpServletRequest.java | 2 +- .../AwsProxyHttpServletRequestReader.java | 4 +- .../testutils/AwsProxyRequestBuilder.java | 9 +++ .../proxy/internal/RequestReaderTest.java | 56 +++++++++++++++++++ .../AwsProxyHttpServletRequestFormTest.java | 54 ++++++++++++++---- .../AwsProxyHttpServletRequestReaderTest.java | 3 +- .../jersey/JerseyAwsProxyRequestReader.java | 28 ++-------- 10 files changed, 213 insertions(+), 40 deletions(-) create mode 100644 aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/ContainerConfig.java create mode 100644 aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/RequestReaderTest.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 aed34c08d..db045a588 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,7 @@ package com.amazonaws.serverless.proxy.internal; +import com.amazonaws.serverless.proxy.internal.model.ContainerConfig; import com.amazonaws.services.lambda.runtime.Context; import javax.ws.rs.core.SecurityContext; @@ -45,9 +46,10 @@ public abstract class LambdaContainerHandler securityContextWriter; private ExceptionHandler exceptionHandler; - protected Context lambdaContext; + private ContainerConfig config = ContainerConfig.defaultConfig(); + //------------------------------------------------------------- // Constructors @@ -79,6 +81,11 @@ protected abstract void handleRequest(ContainerRequestType containerRequest, Con // Methods - Public //------------------------------------------------------------- + public void stripBasePath(String basePath) { + config.setStripBasePath(true); + config.setServiceBasePath(basePath); + } + /** * Proxies requests to the underlying container given the incoming Lambda request. This method returns a populated * return object for the Lambda function. @@ -93,7 +100,7 @@ public ResponseType proxy(RequestType request, Context context) { SecurityContext securityContext = securityContextWriter.writeSecurityContext(request, context); CountDownLatch latch = new CountDownLatch(1); ContainerResponseType containerResponse = getContainerResponse(latch); - ContainerRequestType containerRequest = requestReader.readRequest(request, securityContext, context); + ContainerRequestType containerRequest = requestReader.readRequest(request, securityContext, context, config); handleRequest(containerRequest, containerResponse, context); 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/internal/RequestReader.java index 973f6f98f..198e33e89 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/internal/RequestReader.java @@ -14,6 +14,7 @@ import com.amazonaws.serverless.exceptions.InvalidRequestEventException; +import com.amazonaws.serverless.proxy.internal.model.ContainerConfig; import com.amazonaws.services.lambda.runtime.Context; import com.fasterxml.jackson.databind.ObjectMapper; @@ -67,9 +68,36 @@ public abstract class RequestReader { * @return A valid request object for the underlying container * @throws InvalidRequestEventException This exception is thrown if anything goes wrong during the creation of the request object */ - protected abstract ContainerRequestType readRequest(RequestType request, SecurityContext securityContext, Context lambdaContext) + protected abstract ContainerRequestType readRequest(RequestType request, SecurityContext securityContext, Context lambdaContext, ContainerConfig config) throws InvalidRequestEventException; protected abstract Class getRequestClass(); + + + //------------------------------------------------------------- + // Methods - Protected + //------------------------------------------------------------- + + /** + * Strips the base path from the request path if the container configuration object requires it + * @param requestPath The incoming request path + * @param config The container configuration object + * @return The final request path + */ + protected String stripBasePath(String requestPath, ContainerConfig config) { + if (!config.isStripBasePath()) { + return requestPath; + } + + if (requestPath.startsWith(config.getServiceBasePath())) { + String newRequestPath = requestPath.replaceFirst(config.getServiceBasePath(), ""); + if (!newRequestPath.startsWith("/")) { + newRequestPath = "/" + newRequestPath; + } + return newRequestPath; + } + + return requestPath; + } } diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/ContainerConfig.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/ContainerConfig.java new file mode 100644 index 000000000..54df45a65 --- /dev/null +++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/model/ContainerConfig.java @@ -0,0 +1,56 @@ +package com.amazonaws.serverless.proxy.internal.model; + + +/** + * Configuration paramters used by the RequestReader 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 22658f3df..5accb191a 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 e5e381b22..469c4ce60 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 RequestReader { - //------------------------------------------------------------- - // Variables - Private - //------------------------------------------------------------- - - private boolean useStageAsBasePath = false; - - //------------------------------------------------------------- // Variables - Private - Static //------------------------------------------------------------- @@ -69,14 +63,16 @@ public class JerseyAwsProxyRequestReader extends RequestReaderfalse - * @param useStageAsBasePath True if the stage name should be included in the request path, false otherwise - */ - public void setUseStageAsBasePath(boolean useStageAsBasePath) { - this.useStageAsBasePath = useStageAsBasePath; - } - - //------------------------------------------------------------- // Methods - Protected //------------------------------------------------------------- From 54104b499c69ec222bf891d896554de9000bdc7b Mon Sep 17 00:00:00 2001 From: sapessi Date: Tue, 20 Jun 2017 11:48:10 -0700 Subject: [PATCH 3/3] More unit tests and final fixes Changed the ContainerConfig property to be static --- .../internal/LambdaContainerHandler.java | 29 ++++++++++++++++++- .../proxy/internal/RequestReaderTest.java | 21 ++++++++++++++ .../AwsProxyHttpServletRequestReaderTest.java | 2 +- .../AwsProxyServletRequestFactory.java | 7 +++-- 4 files changed, 55 insertions(+), 4 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 db045a588..f924576da 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 @@ -37,6 +37,7 @@ public abstract class LambdaContainerHandler