From 9e99ee759bd8c72b7b3ca3e4f9e1de704acfd0d8 Mon Sep 17 00:00:00 2001 From: Jonathan Demirgian Date: Wed, 28 Apr 2021 11:15:12 -0400 Subject: [PATCH] Use Content-Encoding to identify if data is binary When using _whitenoise_ for caching, which provides compression, binary types may include mimetypes, "text/", "application/json": - response.mimetype.startswith("text/") - response.mimetype == "application/json" Assuming that Content-Encoding will be set (as whitenoise apparently does) this allows compression to be applied by the application for "text/" and "application/json". About Content-Encoding: developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding --- The above commit message and functional code change to zappa/handler.py was written by GH user @monkut and a PR was provided with https://github.com/Miserlou/Zappa/pull/2170. That PR was not merged in before the fork from Miserlou/zappa to Zappa/zappa. This commit copies the code from that PR, adds a comment line referencing the new migrated issue, and additionally adds tests to confirm behavior. Co-authored-by: monkut --- tests/test_handler.py | 18 ++++++++++++++++++ tests/test_wsgi_binary_support_app.py | 18 ++++++++++++++++++ zappa/handler.py | 8 +++++++- 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/tests/test_handler.py b/tests/test_handler.py index fcbbb032c..4736a31f0 100644 --- a/tests/test_handler.py +++ b/tests/test_handler.py @@ -297,6 +297,24 @@ def test_wsgi_script_binary_support_base64_behavior(self): self.assertTrue(response['isBase64Encoded']) self.assertTrue(is_base64(response['body'])) + content_encoded_json = {**text_plain_event, **{'path': '/content_encoding_header_json1'}} + + response = lh.handler(content_encoded_json, None) + + self.assertEqual(response['statusCode'], 200) + self.assertIn('isBase64Encoded', response) + self.assertTrue(response['isBase64Encoded']) + self.assertTrue(is_base64(response['body'])) + + content_encoded_text_arbitrary = {**text_plain_event, **{'path': '/content_encoding_header_textarbitrary1'}} + + response = lh.handler(content_encoded_text_arbitrary, None) + + self.assertEqual(response['statusCode'], 200) + self.assertIn('isBase64Encoded', response) + self.assertTrue(response['isBase64Encoded']) + self.assertTrue(is_base64(response['body'])) + def test_wsgi_script_on_cognito_event_request(self): """ Ensure that requests sent by cognito behave sensibly diff --git a/tests/test_wsgi_binary_support_app.py b/tests/test_wsgi_binary_support_app.py index 51a1989e9..212fa4f8d 100644 --- a/tests/test_wsgi_binary_support_app.py +++ b/tests/test_wsgi_binary_support_app.py @@ -28,3 +28,21 @@ def json_mimetype_response_1(): @app.route('/arbitrarybinary_mimetype_response1', methods=['GET']) def arbitrary_mimetype_response_1(): return Response(response=b"some binary data", mimetype="arbitrary/binary_mimetype") + + +@app.route('/content_encoding_header_json1', methods=['GET']) +def response_with_content_encoding_mimetype1(): + return Response( + response=json.dumps({'some': 'data'}), + mimetype="application/json", + headers={'Content-Encoding': 'gzip'} + ) + + +@app.route('/content_encoding_header_textarbitrary1', methods=['GET']) +def response_with_content_encoding_mimetype2(): + return Response( + response="OK", + mimetype="text/arbitrary", + headers={'Content-Encoding': 'shouldnt_matter'} + ) diff --git a/zappa/handler.py b/zappa/handler.py index 1474cf453..4f27f5775 100644 --- a/zappa/handler.py +++ b/zappa/handler.py @@ -551,7 +551,13 @@ def handler(self, event, context): zappa_returndict.setdefault('statusDescription', response.status) if response.data: - if settings.BINARY_SUPPORT and \ + if settings.BINARY_SUPPORT and response.headers.get("Content-Encoding"): + # We could have a text response that's gzip + # encoded. Therefore, we base-64 encode it. + # Related: https://github.com/zappa/Zappa/issues/908 + zappa_returndict['body'] = base64.b64encode(response.data).decode('utf-8') + zappa_returndict["isBase64Encoded"] = True + elif settings.BINARY_SUPPORT and \ not response.mimetype.startswith("text/") \ and response.mimetype != "application/json": zappa_returndict['body'] = base64.b64encode(response.data).decode('utf-8')