Skip to content

fix(event_handler): fix prefix stripping, gzip negotiation, JSON detection and CORS Vary - #8638

Merged
leandrodamascena merged 9 commits into
aws-powertools:developfrom
Oakcube4life:fix/api-gateway-routing-response
Oct 24, 2026
Merged

leandrodamascena merged 9 commits into
aws-powertools:developfrom
Oakcube4life:fix/api-gateway-routing-response

Conversation

@Oakcube4life

@Oakcube4life Oakcube4life commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Issue number: closes #8580

Summary

Changes

One fix plus tests per bug, one commit each, in event_handler/api_gateway.py. Item 1 (escaping route rules) was dropped after review, because regex in route rules is a documented feature; a test for the documented catch-all routes was added instead.

  1. Regex strip_prefixes: re.sub(prefix, "", path) removed every match anywhere in the path, so /v1/users/v2 with re.compile(r"/v[1-9]+") became /users and returned 404. It now uses prefix.match(path) and strips a single leading match.
  2. gzip with q=0: the check was a substring test, so gzip;q=0 was still compressed and GZIP was not. Accept-Encoding is now split into codings, compared case-insensitively, and a zero q-value is treated as a refusal.
  3. Bytes body with a JSON content type: a bytes body was passed to the JSON serializer and raised TypeError. The serializer now skips bytes, which are base64 encoded as the docstring says.
  4. is_json() casing: Response.is_json() only found Content-Type with exact casing, so headers={"content-type": "application/json"} returned the dict unserialized. The header name is now matched in any casing.
  5. Missing Vary: Origin: CORSConfig.to_dict now adds Vary: Origin when a specific origin is echoed. A Vary value already set by the route is kept and Origin is appended. Wildcard (*) responses are unchanged.

Catch-all routes test: test_route_rule_supports_documented_catch_all_regex covers the documented .+, /files/.+ and /users/<user_id>/files/.+ rules, including that user_id is still captured. It fails if route rules are escaped, so the regression from item 1 can't come back unnoticed.

User experience

Each repro for items 2 to 6 from the issue snippet now behaves as expected: a regex prefix is stripped once from the start, gzip;q=0 gets an uncompressed response, a JSON bytes body is returned base64 encoded, a lowercase content-type header serializes the body, and a response echoing a specific origin carries Vary: Origin. Documented catch-all routes keep working and are now covered by tests.

14 test cases added (8 test functions); each fix's tests were verified to fail before the fix. The full test suite passes, and ruff, mypy and the bandit baseline are clean.


By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@Oakcube4life
Oakcube4life requested a review from a team as a code owner October 19, 2026 13:23
@boring-cyborg

boring-cyborg Bot commented Oct 19, 2026

Copy link
Copy Markdown

Thanks a lot for your first contribution! Please check out our contributing guidelines and don't hesitate to ask whatever you need.
In the meantime, check out the #python channel on our Powertools for AWS Lambda Discord: Invite link

@powertools-for-aws-oss-automation powertools-for-aws-oss-automation Bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Oct 19, 2026

@leandrodamascena leandrodamascena left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Oakcube4life, really good work on this, and thanks for flagging item 1 instead of just shipping it. You were right to ask: regex in route rules is a documented feature (the "Catch-all routes" section and dynamic_routes_catch_all.py rely on .+, .* and /users/<user_id>/files/.+), and with the escaping those routes return 404. That one is on me, I listed it in the issue without checking the docs, I'm correcting #8580 now.

Could you drop item 1 and its two tests, keep items 2 to 6 exactly as they are, and, since you're there, add a test for the documented catch-all routes (.+, /files/.+, /users/<user_id>/files/.+)? We have none today, which is how I missed it. Everything else checks out: I ran the suite and probed strip_prefixes, gzip negotiation, bytes bodies, lowercase content-type and Vary, all behave.

@leandrodamascena leandrodamascena added this to the 3.38.0 milestone Oct 22, 2026
@Oakcube4life Oakcube4life changed the title fix(event_handler): fix route matching, prefix stripping, gzip negotiation and CORS Vary fix(event_handler): fix prefix stripping, gzip negotiation, JSON detection and CORS Vary Oct 22, 2026
@Oakcube4life

Oakcube4life commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks for the review! Done: item 1 is reverted (d1b797c), items 2–6 are unchanged, and test_route_rule_supports_documented_catch_all_regex covers .+, /files/.+ and /users/<user_id>/files/.+, including that user_id is still captured. I checked that it fails if route rules are escaped, so that regression can't slip back in. I've updated the title and description to match.

@leandrodamascena leandrodamascena left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Oakcube4life, that was fast. I went through items 2 to 6 again, this time behind a real API Gateway REST API: curl --compressed gets gzip, gzip;q=0 does not, a bytes JSON body comes back intact, lowercase content-type serializes, specific origins carry Vary: Origin while * stays clean, and the documented catch-all routes still match with user_id captured. The new catch-all test is exactly what I wanted to see.

One tiny thing, not worth another round: a route that already has Origin in its own Vary ends up with Origin twice. Harmless, we can dedupe later if anyone cares.

Approving. This goes out in 3.38.0 and I'll merge it shortly. Thanks for the patience with the back and forth on item 1.

@codecov

codecov Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.71429% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 96.94%. Comparing base (05877b2) to head (eb20bbf).
⚠️ Report is 1 commits behind head on develop.

Files with missing lines Patch % Lines
aws_lambda_powertools/event_handler/api_gateway.py 85.71% 3 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #8638      +/-   ##
===========================================
- Coverage    96.95%   96.94%   -0.02%     
===========================================
  Files          318      318              
  Lines        16102    16121      +19     
  Branches      1440     1445       +5     
===========================================
+ Hits         15612    15628      +16     
- Misses         349      352       +3     
  Partials       141      141              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sonarqubecloud

Copy link
Copy Markdown

@leandrodamascena
leandrodamascena merged commit 45f7a4d into aws-powertools:develop Oct 24, 2026
12 of 14 checks passed
@boring-cyborg

boring-cyborg Bot commented Oct 24, 2026

Copy link
Copy Markdown

Awesome work, congrats on your first merged pull request and thank you for helping improve everyone's experience!

@powertools-for-aws-oss-automation

Copy link
Copy Markdown

Awesome work, congrats on your first merged pull request and thank you for helping improve everyone's experience!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

event_handlers size/L Denotes a PR that changes 100-499 lines, ignoring generated files. tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: routing and Response issues: unescaped route regex, regex strip_prefixes, gzip q=0, bytes JSON body, is_json() casing, missing Vary: Origin

2 participants