Skip to content

Commit b49b932

Browse files
committed
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
1 parent 37653e0 commit b49b932

File tree

29 files changed

+464
-70
lines changed

29 files changed

+464
-70
lines changed

aws-serverless-java-container-core/pom.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@
6565
<version>1.10.19</version>
6666
<scope>test</scope>
6767
</dependency>
68+
69+
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpmime -->
70+
<dependency>
71+
<groupId>org.apache.httpcomponents</groupId>
72+
<artifactId>httpmime</artifactId>
73+
<version>4.5.3</version>
74+
<scope>test</scope>
75+
</dependency>
76+
6877
</dependencies>
6978

7079
</project>

aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandler.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ public abstract class LambdaContainerHandler<RequestType, ResponseType, Containe
4646
private ExceptionHandler<ResponseType> exceptionHandler;
4747

4848

49+
protected Context lambdaContext;
50+
51+
4952
//-------------------------------------------------------------
5053
// Constructors
5154
//-------------------------------------------------------------
@@ -85,6 +88,7 @@ protected abstract void handleRequest(ContainerRequestType containerRequest, Con
8588
* @return A valid response type
8689
*/
8790
public ResponseType proxy(RequestType request, Context context) {
91+
lambdaContext = context;
8892
try {
8993
SecurityContext securityContext = securityContextWriter.writeSecurityContext(request, context);
9094
CountDownLatch latch = new CountDownLatch(1);
@@ -98,10 +102,6 @@ public ResponseType proxy(RequestType request, Context context) {
98102
return responseWriter.writeResponse(containerResponse, context);
99103
} catch (Exception e) {
100104
context.getLogger().log("Error while handling request: " + e.getMessage());
101-
102-
/*for (StackTraceElement el : e.getStackTrace()) {
103-
context.getLogger().log(el.toString());
104-
}*/
105105
e.printStackTrace();
106106

107107
return exceptionHandler.handle(e);

aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletRequest.java

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,13 @@
2323
import java.io.UnsupportedEncodingException;
2424
import java.net.URLEncoder;
2525
import java.nio.charset.StandardCharsets;
26-
import java.util.*;
26+
import java.util.AbstractMap;
27+
import java.util.ArrayList;
28+
import java.util.Collections;
29+
import java.util.Enumeration;
30+
import java.util.HashMap;
31+
import java.util.List;
32+
import java.util.Map;
2733
import java.util.stream.Collectors;
2834

2935

@@ -56,6 +62,9 @@ public abstract class AwsHttpServletRequest implements HttpServletRequest {
5662

5763
private Context lambdaContext;
5864
private Map<String, Object> attributes;
65+
private ServletContext servletContext;
66+
67+
protected DispatcherType dispatcherType;
5968

6069

6170
//-------------------------------------------------------------
@@ -72,6 +81,7 @@ public abstract class AwsHttpServletRequest implements HttpServletRequest {
7281
attributes = new HashMap<>();
7382
}
7483

84+
7585
//-------------------------------------------------------------
7686
// Implementation - HttpServletRequest
7787
//-------------------------------------------------------------
@@ -119,6 +129,7 @@ public boolean isRequestedSessionIdFromURL() {
119129

120130

121131
@Override
132+
@Deprecated
122133
public boolean isRequestedSessionIdFromUrl() {
123134
return false;
124135
}
@@ -185,7 +196,7 @@ public int getLocalPort() {
185196

186197
@Override
187198
public ServletContext getServletContext() {
188-
return AwsServletContext.getInstance(lambdaContext);
199+
return servletContext;
189200
}
190201

191202

@@ -213,6 +224,19 @@ public DispatcherType getDispatcherType() {
213224
}
214225

215226

227+
//-------------------------------------------------------------
228+
// Methods - Getter/Setter
229+
//-------------------------------------------------------------
230+
231+
public void setDispatcherType(DispatcherType type) {
232+
dispatcherType = type;
233+
}
234+
235+
public void setServletContext(ServletContext context) {
236+
servletContext = context;
237+
}
238+
239+
216240
//-------------------------------------------------------------
217241
// Methods - Protected
218242
//-------------------------------------------------------------
@@ -223,7 +247,6 @@ public DispatcherType getDispatcherType() {
223247
* @return An array of Cookie objects from the header
224248
*/
225249
protected Cookie[] parseCookieHeaderValue(String headerValue) {
226-
227250
List<Map.Entry<String, String>> parsedHeaders = this.parseHeaderValue(headerValue);
228251

229252
return parsedHeaders.stream()
@@ -232,6 +255,7 @@ protected Cookie[] parseCookieHeaderValue(String headerValue) {
232255
.toArray(Cookie[]::new);
233256
}
234257

258+
235259
/**
236260
* Given a map of key/values query string parameters from API Gateway, creates a query string as it would have
237261
* been in the original url.

aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletResponse.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ public void setStatus(int i) {
188188

189189

190190
@Override
191+
@Deprecated
191192
public void setStatus(int i, String s) {
192193
statusCode = i;
193194
statusMessage = s;

aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java

Lines changed: 71 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@
1717
import com.amazonaws.serverless.proxy.internal.RequestReader;
1818
import com.amazonaws.serverless.proxy.internal.ResponseWriter;
1919
import com.amazonaws.serverless.proxy.internal.SecurityContextWriter;
20+
import com.amazonaws.services.lambda.runtime.Context;
2021

2122
import javax.servlet.ServletContext;
2223
import javax.servlet.ServletException;
24+
import javax.servlet.ServletRequest;
25+
import javax.servlet.ServletResponse;
2326
import javax.servlet.http.HttpServletRequest;
2427
import javax.servlet.http.HttpServletResponse;
2528
import java.io.IOException;
@@ -28,6 +31,8 @@
2831
* Abstract extension of the code <code>LambdaContainerHandler</code> object that adds protected variables for the
2932
* <code>ServletContext</code> and <code>FilterChainManager</code>. This object should be extended by the framework-specific
3033
* implementations that want to support the servlet 3.1 specs.
34+
*
35+
* Because Lambda only allows one event per container at a time, this object also acts as the <code>RequestDispatcher</code>
3136
* @param <RequestType>
3237
* @param <ResponseType>
3338
* @param <ContainerRequestType>
@@ -42,14 +47,13 @@ public abstract class AwsLambdaServletContainerHandler<RequestType, ResponseType
4247
// Variables - Private
4348
//-------------------------------------------------------------
4449

45-
private FilterChainManager filterChainManager;
50+
protected ServletContext servletContext;
4651

4752
//-------------------------------------------------------------
4853
// Variables - Protected
4954
//-------------------------------------------------------------
50-
51-
protected ServletContext servletContext;
5255
protected StartupHandler startupHandler;
56+
private FilterChainManager<AwsServletContext> filterChainManager;
5357

5458

5559
//-------------------------------------------------------------
@@ -63,30 +67,43 @@ protected AwsLambdaServletContainerHandler(RequestReader<RequestType, ContainerR
6367
super(requestReader, responseWriter, securityContextWriter, exceptionHandler);
6468
}
6569

66-
6770
//-------------------------------------------------------------
68-
// Methods - Getter/Setter
71+
// Methods - Public
6972
//-------------------------------------------------------------
7073

7174
/**
72-
* Returns the current ServletContext. If the framework implementation does not set the value for
73-
* servlet context this method will return null.
74-
* @return The initialized servlet context if the framework-specific implementation requires one, otherwise null
75+
* Fowards a request to the existing framework container. This is called by the <code>AwsProxyRequestDispatcher</code> object
76+
* @param servletRequest The modified request object with the new request path
77+
* @param servletResponse The original servlet response
78+
* @throws ServletException
79+
* @throws IOException
7580
*/
76-
public ServletContext getServletContext() {
77-
return servletContext;
81+
public void forward(ContainerRequestType servletRequest, ContainerResponseType servletResponse)
82+
throws ServletException, IOException {
83+
try {
84+
handleRequest(servletRequest, servletResponse, lambdaContext);
85+
} catch (Exception e) {
86+
e.printStackTrace();
87+
throw new ServletException(e);
88+
}
7889
}
7990

8091

8192
/**
82-
* Sets the ServletContext in the handler and initialized a new <code>FilterChainManager</code>
83-
* @param context An initialized ServletContext
93+
* Includes a request to the existing framework container. This is called by the <code>AwsProxyRequestDispatcher</code> object
94+
* @param servletRequest The modified request object with the new request path
95+
* @param servletResponse The original servlet response
96+
* @throws ServletException
97+
* @throws IOException
8498
*/
85-
protected void setServletContext(final ServletContext context) {
86-
servletContext = context;
87-
// We assume custom implementations of the RequestWriter for HttpServletRequest will reuse
88-
// the existing AwsServletContext object since it has no dependencies other than the Lambda context
89-
filterChainManager = new AwsFilterChainManager((AwsServletContext)context);
99+
public void include(ContainerRequestType servletRequest, ContainerResponseType servletResponse)
100+
throws ServletException, IOException {
101+
try {
102+
handleRequest(servletRequest, servletResponse, lambdaContext);
103+
} catch (Exception e) {
104+
e.printStackTrace();
105+
throw new ServletException(e);
106+
}
90107
}
91108

92109

@@ -111,6 +128,43 @@ public void onStartup(final StartupHandler h) {
111128
startupHandler = h;
112129
}
113130

131+
@Override
132+
protected void handleRequest(ContainerRequestType containerRequest, ContainerResponseType containerResponse, Context lambdaContext)
133+
throws Exception {
134+
// The servlet context should not be linked to a specific request object, only to the Lambda
135+
// context so we only set it once.
136+
// TODO: In the future, if we decide to support multiple servlets/contexts in an instance we only need to modify this method
137+
if (getServletContext() == null) {
138+
setServletContext(new AwsServletContext(lambdaContext, this));
139+
}
140+
}
141+
142+
143+
//-------------------------------------------------------------
144+
// Methods - Getter/Setter
145+
//-------------------------------------------------------------
146+
147+
/**
148+
* Returns the current ServletContext. If the framework implementation does not set the value for
149+
* servlet context this method will return null.
150+
* @return The initialized servlet context if the framework-specific implementation requires one, otherwise null
151+
*/
152+
public ServletContext getServletContext() {
153+
return servletContext;
154+
}
155+
156+
157+
/**
158+
* Sets the ServletContext in the handler and initialized a new <code>FilterChainManager</code>
159+
* @param context An initialized ServletContext
160+
*/
161+
protected void setServletContext(final ServletContext context) {
162+
servletContext = context;
163+
// We assume custom implementations of the RequestWriter for HttpServletRequest will reuse
164+
// the existing AwsServletContext object since it has no dependencies other than the Lambda context
165+
filterChainManager = new AwsFilterChainManager((AwsServletContext)context);
166+
}
167+
114168

115169
//-------------------------------------------------------------
116170
// Methods - Protected

aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequest.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import org.apache.commons.fileupload.FileItem;
2020
import org.apache.commons.fileupload.FileUploadException;
21+
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
2122
import org.apache.commons.fileupload.servlet.ServletFileUpload;
2223

2324
import javax.servlet.AsyncContext;
@@ -89,6 +90,10 @@ public AwsProxyHttpServletRequest(AwsProxyRequest awsProxyRequest, Context lambd
8990
this.multipartFormParameters = getMultipartFormParametersMap();
9091
}
9192

93+
public AwsProxyRequest getAwsProxyRequest() {
94+
return this.request;
95+
}
96+
9297

9398
//-------------------------------------------------------------
9499
// Implementation - HttpServletRequest
@@ -579,11 +584,11 @@ public boolean isSecure() {
579584

580585
@Override
581586
public RequestDispatcher getRequestDispatcher(String s) {
582-
return null;
587+
return getServletContext().getRequestDispatcher(s);
583588
}
584589

585-
586590
@Override
591+
@Deprecated
587592
public String getRealPath(String s) {
588593
// we are in an archive on a remote server
589594
return null;
@@ -655,7 +660,7 @@ private Map<String, Part> getMultipartFormParametersMap() {
655660

656661
Map<String, Part> output = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
657662

658-
ServletFileUpload upload = new ServletFileUpload();
663+
ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
659664
try {
660665
List<FileItem> items = upload.parseRequest(this);
661666
for (FileItem item : items) {

aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestReader.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,13 @@ public AwsProxyHttpServletRequest readRequest(AwsProxyRequest request, SecurityC
3636
servletRequest.setAttribute(API_GATEWAY_CONTEXT_PROPERTY, request.getRequestContext());
3737
servletRequest.setAttribute(API_GATEWAY_STAGE_VARS_PROPERTY, request.getStageVariables());
3838
servletRequest.setAttribute(LAMBDA_CONTEXT_PROPERTY, lambdaContext);
39+
3940
return servletRequest;
4041
}
4142

43+
//-------------------------------------------------------------
44+
// Methods - Protected
45+
//-------------------------------------------------------------
4246

4347
@Override
4448
protected Class<? extends AwsProxyRequest> getRequestClass() {

0 commit comments

Comments
 (0)