Skip to content
Closed
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f1881b6
:wrench: migrate https://github.com/zappa/Zappa/pull/971 to lastest m…
monkut Jul 28, 2022
19f74a9
:art: run black/isort
monkut Jul 28, 2022
d7fcee4
:recycle: refactor to allow for other binary ignore types based on mi…
monkut Jul 28, 2022
039cbfe
:art: run black/fix flake8
monkut Jul 28, 2022
bcdfbe4
:wrench: add EXCEPTION_HANDLER setting
monkut Jul 28, 2022
3f0d135
:bug: fix zappa_returndict["body"] assignment
monkut Jul 28, 2022
583cc4d
:pencil: add temp debug info
monkut Jul 28, 2022
20bd12f
:fire: delete unnecessary print statements
monkut Jul 28, 2022
2a6aacd
:recycle: Update comments and minor refactor for clarity
monkut Jul 28, 2022
153366d
:recycle: refactor for ease of testing and clarity
monkut Jul 29, 2022
4ddfaa5
:art: fix flake8
monkut Jul 29, 2022
0370119
:sparkles: add `additional_text_mimetypes` setting
monkut Aug 5, 2022
71c8aa3
:wrench: Expand default text mimetypes mentioned in https://github.co…
monkut Aug 12, 2022
48057fc
:art: run black/isort
monkut Aug 12, 2022
34e8755
Merge branch 'master' into feature/issue-908-update-binarysupport-han…
monkut Aug 21, 2022
7798bd5
Merge branch 'master' into feature/issue-908-update-binarysupport-han…
monkut Sep 1, 2022
cdb3337
Merge branch 'master' into feature/issue-908-update-binarysupport-han…
monkut Sep 27, 2022
a2370e9
Merge branch 'master' into feature/issue-908-update-binarysupport-han…
monkut Oct 22, 2022
58e221b
:art: run black/isort
monkut Oct 22, 2022
b1bde2a
Merge branch 'master' into feature/issue-908-update-binarysupport-han…
monkut Oct 22, 2022
69f3b71
feat: implement handler for event with format version 2.0
rehanhwr Oct 15, 2022
b389c34
refactor: getting processed response body from new method
rehanhwr Oct 22, 2022
ba0fe42
Merge branch 'master' into handler-for-v2.0-formatted-event
monkut Oct 26, 2022
a7a48a7
Merge branch 'master' into handler-for-v2.0-formatted-event
monkut Nov 13, 2022
06b8bec
fix: lint error
rehanhwr Nov 16, 2022
96590bf
Merge branch 'master' into handler-for-v2.0-formatted-event
rehanhwr Nov 16, 2022
fba5be2
Merge branch 'master' into handler-for-v2.0-formatted-event
monkut Nov 24, 2022
6da3865
chore: move variable initialization before initial if condition
rehanhwr Nov 29, 2022
7548d5f
refactor: abstract implementations to two processing methods
rehanhwr Nov 29, 2022
a4b13e9
fix: determine payload version based on value in the event itself
rehanhwr Nov 29, 2022
ec823b5
Merge branch 'master' into handler-for-v2.0-formatted-event
monkut Dec 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions tests/test_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,6 @@ def test_wsgi_script_binary_support_without_content_encoding_edgecases(
"""
Ensure zappa response bodies are NOT base64 encoded when BINARY_SUPPORT is enabled and the mimetype is "application/json" or starts with "text/".
"""

lh = LambdaHandler("tests.test_binary_support_settings")

text_plain_event = {
Expand Down Expand Up @@ -320,7 +319,6 @@ def test_wsgi_script_binary_support_without_content_encoding(
"""
Ensure zappa response bodies are base64 encoded when BINARY_SUPPORT is enabled and Content-Encoding is absent.
"""

lh = LambdaHandler("tests.test_binary_support_settings")

text_plain_event = {
Expand Down Expand Up @@ -577,3 +575,35 @@ def test_cloudwatch_subscription_event(self):
response = lh.handler(event, None)

self.assertEqual(response, True)

def test_wsgi_script_name_on_v2_formatted_event(self):
"""
Ensure that requests with payload format version 2.0 succeed
"""
lh = LambdaHandler("tests.test_wsgi_script_name_settings")

event = {
"version": "2.0",
"routeKey": "$default",
"rawPath": "/",
"rawQueryString": "",
"headers": {
"host": "1234567890.execute-api.us-east-1.amazonaws.com",
},
"requestContext": {
"http": {
"method": "GET",
"path": "/return/request/url",
},
},
"isBase64Encoded": False,
"body": "",
"cookies": ["Cookie_1=Value1; Expires=21 Oct 2021 07:48 GMT", "Cookie_2=Value2; Max-Age=78000"],
}
response = lh.handler(event, None)

self.assertEqual(response["statusCode"], 200)
self.assertEqual(
response["body"],
"https://1234567890.execute-api.us-east-1.amazonaws.com/dev/return/request/url",
)
1 change: 0 additions & 1 deletion tests/test_wsgi_binary_support_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
This test application exists to confirm how Zappa handles WSGI application
_responses_ when Binary Support is enabled.
"""

import gzip
import json

Expand Down
152 changes: 152 additions & 0 deletions tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1087,6 +1087,158 @@ def test_wsgi_from_apigateway_testbutton(self):
response_tuple = collections.namedtuple("Response", ["status_code", "content"])
response = response_tuple(200, "hello")

def test_wsgi_from_v2_event(self):
event = {
"version": "2.0",
"routeKey": "ANY /{proxy+}",
"rawPath": "/",
"rawQueryString": "",
"headers": {
"accept": "*/*",
"accept-encoding": "gzip, deflate, br",
"accept-language": "en-US,en;q=0.9",
"cache-control": "no-cache",
"content-length": "0",
"dnt": "1",
"host": "qw8klxioji.execute-api.eu-west-1.amazonaws.com",
"pragma": "no-cache",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36",
"x-forwarded-for": "50.191.225.98",
"x-forwarded-port": "443",
"x-forwarded-proto": "https",
},
"requestContext": {
"accountId": "724336686645",
"apiId": "qw8klxioji",
"domainName": "qw8klxioji.execute-api.eu-west-1.amazonaws.com",
"domainPrefix": "qw8klxioji",
"http": {
"method": "GET",
"path": "/",
"protocol": "HTTP/1.1",
"sourceIp": "50.191.225.98",
"userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36",
},
"requestId": "xTG4wqXdSQ0RHpA=",
"routeKey": "ANY /{proxy+}",
"stage": "$default",
"time": "16/Oct/2022:11:17:12 +0000",
"timeEpoch": 1665919032135,
},
"pathParameters": {"proxy": ""},
"isBase64Encoded": False,
}
environ = create_wsgi_request(event, event_version="2.0")
self.assertTrue(environ)

def test_wsgi_from_v2_event_with_lambda_authorizer(self):
principal_id = "user|a1b2c3d4"
authorizer = {"lambda": {"bool": True, "key": "value", "number": 1, "principalId": principal_id}}
event = {
"version": "2.0",
"routeKey": "ANY /{proxy+}",
"rawPath": "/",
"rawQueryString": "",
"headers": {
"accept": "*/*",
"accept-encoding": "gzip, deflate, br",
"authorization": "Bearer 1232314343",
"content-length": "28",
"content-type": "application/json",
"host": "qw8klxioji.execute-api.eu-west-1.amazonaws.com",
"user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36",
"x-forwarded-for": "50.191.225.98",
"x-forwarded-port": "443",
"x-forwarded-proto": "https",
},
"requestContext": {
"accountId": "724336686645",
"apiId": "qw8klxioji",
"authorizer": authorizer,
"domainName": "qw8klxioji.execute-api.eu-west-1.amazonaws.com",
"domainPrefix": "qw8klxioji",
"http": {
"method": "POST",
"path": "/",
"protocol": "HTTP/1.1",
"sourceIp": "50.191.225.98",
"userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36",
},
"requestId": "aJ6Rqi93zQ0GPng=",
"routeKey": "ANY /{proxy+}",
"stage": "$default",
"time": "17/Oct/2022:14:58:44 +0000",
"timeEpoch": 1666018724000,
},
"pathParameters": {"proxy": ""},
"body": "{'data':'0123456789'}",
"isBase64Encoded": False,
}
environ = create_wsgi_request(event, event_version="2.0")
self.assertEqual(environ["API_GATEWAY_AUTHORIZER"], authorizer)
self.assertEqual(environ["REMOTE_USER"], principal_id)

def test_wsgi_from_v2_event_with_iam_authorizer(self):
user_arn = "arn:aws:sts::724336686645:assumed-role/SAMLUSER/user.name"
authorizer = {
"iam": {
"accessKey": "AWSACCESSKEYID",
"accountId": "724336686645",
"callerId": "KFDJSURSUC8FU3ITCWEDJ:user.name",
"cognitoIdentity": None,
"principalOrgId": "aws:PrincipalOrgID",
"userArn": user_arn,
"userId": "KFDJSURSUC8FU3ITCWEDJ:user.name",
}
}
event = {
"version": "2.0",
"routeKey": "ANY /{proxy+}",
"rawPath": "/",
"rawQueryString": "",
"headers": {
"accept": "*/*",
"accept-encoding": "gzip, deflate",
"authorization": "AWS4-HMAC-SHA256 Credential=AWSACCESSKEYID/20221017/eu-west-1/execute-api/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=foosignature",
"content-length": "17",
"content-type": "application/json",
"host": "qw8klxioji.execute-api.eu-west-1.amazonaws.com",
"user-agent": "python-requests/2.28.1",
"x-amz-content-sha256": "foobar",
"x-amz-date": "20221017T150616Z",
"x-amz-security-token": "footoken",
"x-forwarded-for": "50.191.225.98",
"x-forwarded-port": "443",
"x-forwarded-proto": "https",
},
"requestContext": {
"accountId": "724336686645",
"apiId": "qw8klxioji",
"authorizer": authorizer,
"domainName": "qw8klxioji.execute-api.eu-west-1.amazonaws.com",
"domainPrefix": "qw8klxioji",
"http": {
"method": "POST",
"path": "/",
"protocol": "HTTP/1.1",
"sourceIp": "50.191.225.98",
"userAgent": "python-requests/2.28.1",
},
"requestId": "aJ5ZZgeYiQ0Rz-A=",
"routeKey": "ANY /{proxy+}",
"stage": "$default",
"time": "17/Oct/2022:15:06:16 +0000",
"timeEpoch": 1666019176656,
},
"pathParameters": {"proxy": ""},
"body": "{'data': '12345'}",
"isBase64Encoded": False,
}
environ = create_wsgi_request(event, event_version="2.0")
self.assertEqual(environ["API_GATEWAY_AUTHORIZER"], authorizer)
self.assertEqual(environ["REMOTE_USER"], user_arn)

##
# Handler
##
Expand Down
113 changes: 113 additions & 0 deletions zappa/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,119 @@ def handler(self, event, context):
logger.error("Cannot find a function to process the triggered event.")
return result

# This is an HTTP-protocol API Gateway event or Lambda url event with payload format version 2.0
elif "version" in event and event["version"] == "2.0":
try:
time_start = datetime.datetime.now()

script_name = ""
host = event.get("headers", {}).get("host")
if host:
if "amazonaws.com" in host:
logger.debug("amazonaws found in host")
# The path provided in th event doesn't include the
# stage, so we must tell Flask to include the API
# stage in the url it calculates. See https://github.com/Miserlou/Zappa/issues/1014
script_name = f"/{settings.API_STAGE}"
else:
# This is a test request sent from the AWS console
if settings.DOMAIN:
# Assume the requests received will be on the specified
# domain. No special handling is required
pass
else:
# Assume the requests received will be to the
# amazonaws.com endpoint, so tell Flask to include the
# API stage
script_name = f"/{settings.API_STAGE}"

base_path = getattr(settings, "BASE_PATH", None)
environ = create_wsgi_request(
event,
script_name=script_name,
base_path=base_path,
trailing_slash=self.trailing_slash,
binary_support=settings.BINARY_SUPPORT,
context_header_mappings=settings.CONTEXT_HEADER_MAPPINGS,
event_version="2.0",
)

# We are always on https on Lambda, so tell our wsgi app that.
environ["HTTPS"] = "on"
environ["wsgi.url_scheme"] = "https"
environ["lambda.context"] = context
environ["lambda.event"] = event

# Execute the application
with Response.from_app(self.wsgi_app, environ) as response:
response_body = None
response_is_base_64_encoded = False
if response.data:
response_body, response_is_base_64_encoded = self._process_response_body(response, settings=settings)

response_status_code = response.status_code

cookies = []
response_headers = {}
for key, value in response.headers:
if key.lower() == "set-cookie":
cookies.append(value)
else:
if key in response_headers:
updated_value = f"{response_headers[key]},{value}"
response_headers[key] = updated_value
else:
response_headers[key] = value

# Calculate the total response time,
# and log it in the Common Log format.
time_end = datetime.datetime.now()
delta = time_end - time_start
response_time_ms = delta.total_seconds() * 1000
response.content = response.data
common_log(environ, response, response_time=response_time_ms)

return {
"cookies": cookies,
"isBase64Encoded": response_is_base_64_encoded,
"statusCode": response_status_code,
"headers": response_headers,
"body": response_body,
}
except Exception as e:
# Print statements are visible in the logs either way
print(e)
exc_info = sys.exc_info()
message = (
"An uncaught exception happened while servicing this request. "
"You can investigate this with the `zappa tail` command."
)

# If we didn't even build an app_module, just raise.
if not settings.DJANGO_SETTINGS:
try:
self.app_module
except NameError as ne:
message = "Failed to import module: {}".format(ne.message)

# Call exception handler for unhandled exceptions
exception_handler = self.settings.EXCEPTION_HANDLER
self._process_exception(
exception_handler=exception_handler,
event=event,
context=context,
exception=e,
)

# Return this unspecified exception as a 500, using template that API Gateway expects.
content = collections.OrderedDict()
content["statusCode"] = 500
body = {"message": message}
if settings.DEBUG: # only include traceback if debug is on.
body["traceback"] = traceback.format_exception(*exc_info) # traceback as a list for readability.
content["body"] = json.dumps(str(body), sort_keys=True, indent=4)
return content

Comment thread
rehanhwr marked this conversation as resolved.
# Normal web app flow
try:
# Timing
Expand Down
Loading