Skip to content

Regression in route match regex in 1.18.0 #550

Closed
@moretension

Description

@moretension

The 1.18.0 release resolves the path regex greedy match problem in #520, but introduces a regression in the new regex pattern. To resolve the greedy match issue, #520 matches on the \w character set and on the non-word boundary (\b) to ensure trailing path components following the match aren't captured. Unfortunately, this change also prevents match of path values containing non-word characters. Email addresses, URL-encoded values, and RFC 4122 UUIDs no long match route rules, because @, %, and - are not included in the \w character match set.

Expected Behavior

APIGatewayResolver route rules should match request path parameters containing @, -, and % in addition to word characters. This was the matching behavior in the 1.17.x release.

Current Behavior

No routes are matched if a path parameter contains a non-word character.

Possible Solution

The _NAMED_GROUP_BOUNDARY_PATTERN needs to be modified to include common non-word ASCII characters used in path components. Here's one possible pattern that seems to work while avoiding greedy capture:

r"(?P\1[-%@.:\\w]+)"

As shown in the REPL:

>>> import re
>>> from uuid import uuid4
>>> m = re.match("/api/v1/(?P<test>[-%@\\w]+)/items", f"/api/v1/{uuid4()}/items")
>>> m.groupdict()
{'test': '1f21c0c1-cd19-4775-8033-9e6244d54d79'}
>>> m = re.match("/api/v1/(?P<test>[-%@.:\\w]+)/items", f"/api/v1/user@example.com/items")
>>> m.groupdict()
{'test': 'user@example.com'}

Steps to Reproduce (for bugs)

The following should call the get_user_items() function and print a Response JSON object containing the items object in the body. However, because the path parameter contains a non-word character, no route matches request path, and a 404 Response JSON object is printed instead.

from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver

api = ApiGatewayResolver()

@api.get("/api/v1/<user>/items")
def get_user_items(user):
    return {"items": ["1", "2", "3"]}

def handler(event, context):
    return api.resolve(event, context)

if __name__ == "__main__":
    event = {
        "httpMethod": "GET",
        "path": "/api/v1/user@example.com/items"
    }
    print(handler(event, {}))

This works (albeit with the greedy match problem) in 1.17.x.

Environment

  • Powertools version used: 1.18.0
  • Packaging format (Layers, PyPi): PyPI
  • AWS Lambda function runtime: 3.8

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions