Service
API Gateway V2 (HTTP API)
AWS API Action
Lambda proxy integration invoke (payload format 2.0), and REQUEST authorizer invoke on payload format 1.0.
Expected behavior
A request to /thing/ on an HTTP API delivers the trailing slash to the Lambda, because rawPath is by contract the raw, unmodified path:
Routers commonly treat /thing and /thing/ as distinct routes, so a v2 Lambda needs to be able to tell them apart.
Actual behavior
The slash is stripped and the Lambda receives rawPath: "/thing". A v2 Lambda cannot distinguish /thing from /thing/ at all.
Cause is the same JAX-RS {proxy} path-param binding that #1557 covered for REST APIs. dispatchV2 rebuilds the path from the stripped binding at ApiGatewayExecuteController.java:1219:
String path = "/" + (proxy == null ? "" : proxy);
and that value becomes the event's rawPath at lines 1728 and 1840. buildRequestAuthorizerEventV1 (line 1680, used for v2 authorizers on payload format 1.0) has the same problem in its methodArn, resource, path and requestContext.path.
Impact
#1863 fixed this for REST (v1) APIs, so as of that PR a REST API preserves the trailing slash and an HTTP API silently does not. That inconsistency between the two API types is the confusing part, and it is worth closing so v1 and v2 do not drift.
Suggested fix
preserveTrailingSlash landed in #1863 as a static package-private helper, so v2 can reuse it as-is:
String path = preserveTrailingSlash(
"/" + (proxy == null ? "" : proxy),
uriInfo.getRequestUri().getRawPath());
One caveat: apiGatewayV2Service.findMatchingRoute (called at line 1216 area) would then receive the slash-preserving path, so route matching should keep using the normalized value. That is the same split #1863 made explicit for v1, where the event fields get the slash-preserving path while resource matching and path-parameter extraction stay on the normalized one.
Reproduction
# Create an HTTP API with a Lambda proxy integration on $default, then:
curl -s http://localhost:4566/restapis/<apiId>/<stage>/_user_request_/thing/
# Lambda receives rawPath "/thing", expected "/thing/"
Environment
- Floci version / image tag:
main (1.5.34)
- How you're running Floci:
./mvnw quarkus:dev
Filed as the follow-up @abanna suggested while reviewing #1863, which deliberately scoped itself to v1. Happy to pick this up.
Service
API Gateway V2 (HTTP API)
AWS API Action
Lambda proxy integration invoke (payload format 2.0), and REQUEST authorizer invoke on payload format 1.0.
Expected behavior
A request to
/thing/on an HTTP API delivers the trailing slash to the Lambda, becauserawPathis by contract the raw, unmodified path:{ "rawPath": "/thing/" }Routers commonly treat
/thingand/thing/as distinct routes, so a v2 Lambda needs to be able to tell them apart.Actual behavior
The slash is stripped and the Lambda receives
rawPath: "/thing". A v2 Lambda cannot distinguish/thingfrom/thing/at all.Cause is the same JAX-RS
{proxy}path-param binding that #1557 covered for REST APIs.dispatchV2rebuilds the path from the stripped binding atApiGatewayExecuteController.java:1219:and that value becomes the event's
rawPathat lines 1728 and 1840.buildRequestAuthorizerEventV1(line 1680, used for v2 authorizers on payload format 1.0) has the same problem in itsmethodArn,resource,pathandrequestContext.path.Impact
#1863 fixed this for REST (v1) APIs, so as of that PR a REST API preserves the trailing slash and an HTTP API silently does not. That inconsistency between the two API types is the confusing part, and it is worth closing so v1 and v2 do not drift.
Suggested fix
preserveTrailingSlashlanded in #1863 as a static package-private helper, so v2 can reuse it as-is:One caveat:
apiGatewayV2Service.findMatchingRoute(called at line 1216 area) would then receive the slash-preserving path, so route matching should keep using the normalized value. That is the same split #1863 made explicit for v1, where the event fields get the slash-preserving path while resource matching and path-parameter extraction stay on the normalized one.Reproduction
Environment
main(1.5.34)./mvnw quarkus:devFiled as the follow-up @abanna suggested while reviewing #1863, which deliberately scoped itself to v1. Happy to pick this up.