Skip to content

Commit 8a41e74

Browse files
authored
Merge pull request #98 from awslabs/servlet-improvements
Latest changes preparing for 0.9 release
2 parents d72afa6 + 1fd3cac commit 8a41e74

File tree

46 files changed

+1047
-193
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1047
-193
lines changed

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414
<version>0.9-SNAPSHOT</version>
1515
</parent>
1616

17+
<properties>
18+
<jackson.version>2.9.3</jackson.version>
19+
<jaxrs.version>2.0.1</jaxrs.version>
20+
<servlet.version>3.1.0</servlet.version>
21+
</properties>
22+
1723
<dependencies>
1824
<!-- https://mvnrepository.com/artifact/com.amazonaws/aws-lambda-java-core -->
1925
<dependency>
@@ -26,14 +32,14 @@
2632
<dependency>
2733
<groupId>javax.servlet</groupId>
2834
<artifactId>javax.servlet-api</artifactId>
29-
<version>3.1.0</version>
35+
<version>${servlet.version}</version>
3036
</dependency>
3137

3238
<!-- https://mvnrepository.com/artifact/javax.ws.rs/javax.ws.rs-api -->
3339
<dependency>
3440
<groupId>javax.ws.rs</groupId>
3541
<artifactId>javax.ws.rs-api</artifactId>
36-
<version>2.0.1</version>
42+
<version>${jaxrs.version}</version>
3743
</dependency>
3844

3945
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@
1313
package com.amazonaws.serverless.proxy;
1414

1515
import com.amazonaws.serverless.exceptions.InvalidRequestEventException;
16+
import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
1617
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
1718
import com.amazonaws.serverless.proxy.model.ErrorModel;
1819

1920
import com.fasterxml.jackson.core.JsonProcessingException;
2021
import com.fasterxml.jackson.databind.ObjectMapper;
22+
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonValueFormat;
23+
import com.fasterxml.jackson.databind.ser.std.JsonValueSerializer;
2124
import org.slf4j.Logger;
2225
import org.slf4j.LoggerFactory;
2326

@@ -55,8 +58,6 @@ public class AwsProxyExceptionHandler
5558
//-------------------------------------------------------------
5659

5760
private static Map<String, String> headers = new HashMap<>();
58-
private static ObjectMapper objectMapper = new ObjectMapper();
59-
6061

6162
//-------------------------------------------------------------
6263
// Constructors
@@ -75,6 +76,10 @@ public class AwsProxyExceptionHandler
7576
@Override
7677
public AwsProxyResponse handle(Throwable ex) {
7778
log.error("Called exception handler for:", ex);
79+
80+
// adding a print stack trace in case we have no appender or we are running inside SAM local, where need the
81+
// output to go to the stderr.
82+
ex.printStackTrace();
7883
if (ex instanceof InvalidRequestEventException) {
7984
return new AwsProxyResponse(500, headers, getErrorJson(INTERNAL_SERVER_ERROR));
8085
} else {
@@ -87,7 +92,7 @@ public AwsProxyResponse handle(Throwable ex) {
8792
public void handle(Throwable ex, OutputStream stream) throws IOException {
8893
AwsProxyResponse response = handle(ex);
8994

90-
objectMapper.writeValue(stream, response);
95+
LambdaContainerHandler.getObjectMapper().writeValue(stream, response);
9196
}
9297

9398

@@ -96,8 +101,9 @@ public void handle(Throwable ex, OutputStream stream) throws IOException {
96101
//-------------------------------------------------------------
97102

98103
String getErrorJson(String message) {
104+
99105
try {
100-
return objectMapper.writeValueAsString(new ErrorModel(message));
106+
return LambdaContainerHandler.getObjectMapper().writeValueAsString(new ErrorModel(message));
101107
} catch (JsonProcessingException e) {
102108
log.error("Could not produce error JSON", e);
103109
return "{ \"message\": \"" + message + "\" }";

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.amazonaws.serverless.proxy.SecurityContextWriter;
2121
import com.amazonaws.services.lambda.runtime.Context;
2222

23+
import com.fasterxml.jackson.databind.ObjectMapper;
2324
import org.slf4j.Logger;
2425
import org.slf4j.LoggerFactory;
2526

@@ -64,6 +65,8 @@ public abstract class LambdaContainerHandler<RequestType, ResponseType, Containe
6465
//-------------------------------------------------------------
6566

6667
private static ContainerConfig config = ContainerConfig.defaultConfig();
68+
private static ObjectMapper objectMapper;
69+
6770

6871

6972
//-------------------------------------------------------------
@@ -97,6 +100,13 @@ protected abstract void handleRequest(ContainerRequestType containerRequest, Con
97100
// Methods - Public
98101
//-------------------------------------------------------------
99102

103+
public static ObjectMapper getObjectMapper() {
104+
if (objectMapper == null) {
105+
objectMapper = new ObjectMapper();
106+
}
107+
return objectMapper;
108+
}
109+
100110
/**
101111
* Configures the library to strip a base path from incoming requests before passing them on to the wrapped
102112
* framework. This was added in response to issue #34 (https://github.com/awslabs/aws-serverless-java-container/issues/34).

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

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
1717
import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
18+
import com.amazonaws.serverless.proxy.model.ContainerConfig;
1819
import com.amazonaws.services.lambda.runtime.Context;
1920

2021
import org.apache.commons.fileupload.FileItem;
@@ -79,16 +80,22 @@ public class AwsProxyHttpServletRequest extends AwsHttpServletRequest {
7980
private Map<String, List<String>> urlEncodedFormParameters;
8081
private Map<String, Part> multipartFormParameters;
8182
private Logger log = LoggerFactory.getLogger(AwsProxyHttpServletRequest.class);
83+
private ContainerConfig config;
8284

8385

8486
//-------------------------------------------------------------
8587
// Constructors
8688
//-------------------------------------------------------------
8789

8890
public AwsProxyHttpServletRequest(AwsProxyRequest awsProxyRequest, Context lambdaContext, SecurityContext awsSecurityContext) {
91+
this(awsProxyRequest, lambdaContext, awsSecurityContext, ContainerConfig.defaultConfig());
92+
}
93+
94+
public AwsProxyHttpServletRequest(AwsProxyRequest awsProxyRequest, Context lambdaContext, SecurityContext awsSecurityContext, ContainerConfig config) {
8995
super(lambdaContext);
9096
this.request = awsProxyRequest;
9197
this.securityContext = awsSecurityContext;
98+
this.config = config;
9299

93100
this.urlEncodedFormParameters = getFormUrlEncodedParametersMap();
94101
this.multipartFormParameters = getMultipartFormParametersMap();
@@ -181,10 +188,7 @@ public String getMethod() {
181188

182189
@Override
183190
public String getPathInfo() {
184-
String pathInfo = getServletPath().replace(getContextPath(), "");
185-
if (!pathInfo.startsWith("/")) {
186-
pathInfo = "/" + pathInfo;
187-
}
191+
String pathInfo = cleanUri(request.getPath()); //getServletPath().replace(getContextPath(), "");
188192
return decodeRequestPath(pathInfo, LambdaContainerHandler.getContainerConfig());
189193
}
190194

@@ -196,13 +200,18 @@ public String getPathTranslated() {
196200
}
197201

198202

199-
/**
200-
* In AWS API Gateway, stage is never given as part of the path.
201-
* @return
202-
*/
203203
@Override
204204
public String getContextPath() {
205-
return "";
205+
if (config.isUseStageAsServletContext()) {
206+
String contextPath = cleanUri(request.getRequestContext().getStage());
207+
if (config.getServiceBasePath() != null) {
208+
contextPath += cleanUri(config.getServiceBasePath());
209+
}
210+
211+
return contextPath;
212+
} else {
213+
return "" + (config.getServiceBasePath() != null ? cleanUri(config.getServiceBasePath()) : "");
214+
}
206215
}
207216

208217

@@ -232,28 +241,25 @@ public Principal getUserPrincipal() {
232241

233242
@Override
234243
public String getRequestURI() {
235-
return (getContextPath().isEmpty() ? "" : "/" + getContextPath()) + request.getPath();
244+
return cleanUri(getContextPath()) + cleanUri(request.getPath());
236245
}
237246

238247

239248
@Override
240249
public StringBuffer getRequestURL() {
241250
String url = "";
242251
url += getServerName();
243-
url += "/";
244-
url += getContextPath();
245-
url += "/";
246-
url += request.getPath();
247-
248-
url = url.replaceAll("/+", "/");
252+
url += cleanUri(getContextPath());
253+
url += cleanUri(request.getPath());
249254

250255
return new StringBuffer(getScheme() + "://" + url);
251256
}
252257

253258

254259
@Override
255260
public String getServletPath() {
256-
return decodeRequestPath(request.getPath(), LambdaContainerHandler.getContainerConfig());
261+
// we always work on the root path
262+
return "";
257263
}
258264

259265
@Override
@@ -494,7 +500,7 @@ public Map<String, String[]> getParameterMap() {
494500

495501
if (request.getQueryStringParameters() != null) {
496502
for (Map.Entry<String, String> entry : request.getQueryStringParameters().entrySet()) {
497-
if (params.containsKey(entry.getKey())) {
503+
if (params.containsKey(entry.getKey()) && !params.get(entry.getKey()).contains(entry.getValue())) {
498504
params.get(entry.getKey()).add(entry.getValue());
499505
} else {
500506
List<String> valueList = new ArrayList<>();
@@ -653,15 +659,15 @@ private String getQueryStringParameterCaseInsensitive(String key) {
653659

654660

655661
private String[] getFormBodyParameterCaseInsensitive(String key) {
656-
List<String> values = urlEncodedFormParameters.get(key);
657-
if (values != null) {
658-
String[] valuesArray = new String[values.size()];
659-
valuesArray = values.toArray(valuesArray);
660-
return valuesArray;
661-
} else {
662-
return null;
663-
}
662+
List<String> values = urlEncodedFormParameters.get(key);
663+
if (values != null) {
664+
String[] valuesArray = new String[values.size()];
665+
valuesArray = values.toArray(valuesArray);
666+
return valuesArray;
667+
} else {
668+
return null;
664669
}
670+
}
665671

666672

667673
private Map<String, Part> getMultipartFormParametersMap() {
@@ -698,6 +704,22 @@ private Map<String, Part> getMultipartFormParametersMap() {
698704
return output;
699705
}
700706

707+
private String cleanUri(String uri) {
708+
String finalUri = uri;
709+
710+
if (!finalUri.startsWith("/")) {
711+
finalUri = "/" + finalUri;
712+
}
713+
714+
if (finalUri.endsWith(("/"))) {
715+
finalUri = finalUri.substring(0, finalUri.length() - 1);
716+
}
717+
718+
finalUri = finalUri.replaceAll("/+", "/");
719+
720+
return finalUri;
721+
}
722+
701723

702724
private Map<String, List<String>> getFormUrlEncodedParametersMap() {
703725
String contentType = getContentType();

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public class AwsProxyHttpServletRequestReader extends RequestReader<AwsProxyRequ
3434
public AwsProxyHttpServletRequest readRequest(AwsProxyRequest request, SecurityContext securityContext, Context lambdaContext, ContainerConfig config)
3535
throws InvalidRequestEventException {
3636
request.setPath(stripBasePath(request.getPath(), config));
37-
AwsProxyHttpServletRequest servletRequest = new AwsProxyHttpServletRequest(request, lambdaContext, securityContext);
37+
AwsProxyHttpServletRequest servletRequest = new AwsProxyHttpServletRequest(request, lambdaContext, securityContext, config);
3838
servletRequest.setAttribute(API_GATEWAY_CONTEXT_PROPERTY, request.getRequestContext());
3939
servletRequest.setAttribute(API_GATEWAY_STAGE_VARS_PROPERTY, request.getStageVariables());
4040
servletRequest.setAttribute(LAMBDA_CONTEXT_PROPERTY, lambdaContext);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public static void clearServletContextCache() {
104104
@Override
105105
public String getContextPath() {
106106
// servlets are always at the root.
107-
return "/";
107+
return "";
108108
}
109109

110110

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public abstract class FilterChainManager<ServletContextType extends ServletConte
9393
* @return A <code>FilterChainHolder</code> object that can be used to apply the filters to the request
9494
*/
9595
FilterChainHolder getFilterChain(final HttpServletRequest request, Servlet servlet) {
96-
String targetPath = request.getServletPath();
96+
String targetPath = request.getRequestURI();
9797
DispatcherType type = request.getDispatcherType();
9898

9999
// only return the cached result if the filter list hasn't changed in the meanwhile

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

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
*/
1313
package com.amazonaws.serverless.proxy.internal.testutils;
1414

15+
import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
1516
import com.amazonaws.serverless.proxy.model.ApiGatewayAuthorizerContext;
1617
import com.amazonaws.serverless.proxy.model.ApiGatewayRequestContext;
1718
import com.amazonaws.serverless.proxy.model.ApiGatewayRequestIdentity;
@@ -41,8 +42,6 @@ public class AwsProxyRequestBuilder {
4142
//-------------------------------------------------------------
4243

4344
private AwsProxyRequest request;
44-
private ObjectMapper mapper;
45-
4645

4746
//-------------------------------------------------------------
4847
// Constructors
@@ -59,7 +58,6 @@ public AwsProxyRequestBuilder(String path) {
5958

6059

6160
public AwsProxyRequestBuilder(String path, String httpMethod) {
62-
this.mapper = new ObjectMapper();
6361

6462
this.request = new AwsProxyRequest();
6563
this.request.setHeaders(new HashMap<>()); // avoid NPE
@@ -144,7 +142,7 @@ public AwsProxyRequestBuilder body(String body) {
144142
public AwsProxyRequestBuilder body(Object body) {
145143
if (request.getHeaders() != null && request.getHeaders().get(HttpHeaders.CONTENT_TYPE).equals(MediaType.APPLICATION_JSON)) {
146144
try {
147-
return body(mapper.writeValueAsString(body));
145+
return body(LambdaContainerHandler.getObjectMapper().writeValueAsString(body));
148146
} catch (JsonProcessingException e) {
149147
throw new UnsupportedOperationException("Could not serialize object: " + e.getMessage());
150148
}
@@ -239,13 +237,13 @@ public AwsProxyRequestBuilder serverName(String serverName) {
239237

240238
public AwsProxyRequestBuilder fromJsonString(String jsonContent)
241239
throws IOException {
242-
request = mapper.readValue(jsonContent, AwsProxyRequest.class);
240+
request = LambdaContainerHandler.getObjectMapper().readValue(jsonContent, AwsProxyRequest.class);
243241
return this;
244242
}
245243

246244
public AwsProxyRequestBuilder fromJsonPath(String filePath)
247245
throws IOException {
248-
request = mapper.readValue(new File(filePath), AwsProxyRequest.class);
246+
request = LambdaContainerHandler.getObjectMapper().readValue(new File(filePath), AwsProxyRequest.class);
249247
return this;
250248
}
251249

0 commit comments

Comments
 (0)