Skip to content

AwsHttpApiV2ProxyHttpServletRequest.getParameterMap returns arrays containing null for empty query params #427

@bogdanb

Description

@bogdanb

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)

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions