-
Notifications
You must be signed in to change notification settings - Fork 566
Description
To help us debug your issue fill in the basic information below using the options provided
Serverless Java Container version: 1.6
Implementations: Spring Boot 2
Framework version: eg SpringBoot 2.5.3
Frontend service: REST API / HTTP API / ALB
Deployment method: Console
Scenario
I’m using a SpringBootLambdaContainerHandler<HttpApiV2ProxyRequest, AwsProxyResponse>
to run a (pre-existing) Spring Boot application in AWS Lambda. The app receives pretty simple REST requests (via CloudFront). For example, a snippet of an event received looks like this:
{
"version": "2.0",
"routeKey": "$default",
"rawPath": "/rest/[...snip...]",
"rawQueryString": "query=&maxResults=20",
"queryStringParameters": {
"maxResults": "20",
"query": ""
},
[...snip...]
}
Expected behavior
I expected the existing app to work more-or-less as it does outside Lambda, when it receives the REST requests directly.
Actual behavior
In practice, I got a crash, in library code outside my control. I think it’s caused by a bug in aws-sjc. What happens is that for the request above, AwsHttpApiV2ProxyHttpServletRequest.getParameterMap
returns a HashMap
which looks kind of like this:
{maxResults=[20], query=[null]}
To be precise, the value for the query
parameter is a String[]
array that contains a single null
value.
The crash happens because the code that consumes the parameter map assumes that the array values will not contain null
. My understanding is that it should not (instead, the array should have contained an empty string), and the JavaDoc of the ServletRequest.getParameterMap
method seems to agree:
Returns: an immutable java.util.Map containing parameter names as keys and parameter values as map values. The keys in the parameter map are of type String. The values in the parameter map are of type String array.
AwsHttpApiV2ProxyHttpServletRequest
seems to be doubly-wrong: First, because it returns null
in the query
array, instead of an empty string. Second (and maybe less importantly), because the returned Map is not immutable.
Steps to reproduce
Include something like this in any Spring Boot application, use SpringBootProxyHandlerBuilder
to pass requests from CloudFront to it, and try any request that contains a query parameter with the empty value:
@Component @Order(Ordered.HIGHEST_PRECEDENCE) @Slf4j
public class ParametersDebuggingFilter extends OncePerRequestFilter
{
@Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response,
final FilterChain filterChain) throws ServletException, IOException
{
log.debug("Request parameter map: {} (class: {}, req: {})",
Maps.transformValues(request.getParameterMap(),
array -> array == null ? null : "%s@%d".formatted(Arrays.asList(array), array.length)),
request.getParameterMap().getClass(), request.getClass());
filterChain.doFilter(request, response);
}
}
Full log output
For the above request, the output in my case looks like this:
Request parameter map: {maxResults=[20]@1, query=[null]@1} (class: class java.util.HashMap, req: class com.amazonaws.serverless.proxy.internal.servlet.AwsHttpApiV2ProxyHttpServletRequest)