diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a95fff266..7c6f2e89b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -11,6 +11,9 @@ Next Release (TBD) * Fix issue where ``api_gateway_stage`` was being ignored when set in the ``config.json`` file (`#495 `__) +* Fix bug where ``raw_body`` would raise an exception if no HTTP + body was provided + (`#503 `__) 1.0.1 diff --git a/chalice/app.py b/chalice/app.py index 13090c86e..3e5b3f6a2 100644 --- a/chalice/app.py +++ b/chalice/app.py @@ -277,7 +277,7 @@ def _base64decode(self, encoded): @property def raw_body(self): - if self._raw_body is None: + if self._raw_body is None and self._body is not None: if self._is_base64_encoded: self._raw_body = self._base64decode(self._body) elif not isinstance(self._body, bytes): diff --git a/chalice/local.py b/chalice/local.py index 7e0b04e4f..43b636608 100644 --- a/chalice/local.py +++ b/chalice/local.py @@ -99,9 +99,7 @@ def create_lambda_event(self, method, path, headers, body=None): 'pathParameters': view_route.captured, 'stageVariables': {}, } - if body is None: - event['body'] = '{}' - elif self._is_binary(headers): + if self._is_binary(headers) and body is not None: event['body'] = base64.b64encode(body).decode('ascii') event['isBase64Encoded'] = True else: diff --git a/tests/integration/test_features.py b/tests/integration/test_features.py index 3caa80924..5f3bd10a5 100644 --- a/tests/integration/test_features.py +++ b/tests/integration/test_features.py @@ -406,6 +406,13 @@ def test_can_use_shared_auth(smoke_test_app): assert context['authorizer']['foo'] == 'bar' +def test_empty_raw_body(smoke_test_app): + url = smoke_test_app.url + '/repr-raw-body' + response = requests.post(url) + response.raise_for_status() + assert response.json() == {'repr-raw-body': 'None'} + + @pytest.mark.on_redeploy def test_redeploy_no_change_view(smoke_test_app): smoke_test_app.redeploy_once() diff --git a/tests/integration/testapp/app.py b/tests/integration/testapp/app.py index d12205acf..75e03b208 100644 --- a/tests/integration/testapp/app.py +++ b/tests/integration/testapp/app.py @@ -180,3 +180,8 @@ def fake_profile_read_only(): methods=['POST']) def fake_profile_post(): return {'success': True, 'context': app.current_request.context} + + +@app.route('/repr-raw-body', methods=['POST']) +def repr_raw_body(): + return {'repr-raw-body': repr(app.current_request.raw_body)} diff --git a/tests/unit/test_app.py b/tests/unit/test_app.py index fc9cdae36..f42f2e884 100644 --- a/tests/unit/test_app.py +++ b/tests/unit/test_app.py @@ -1278,3 +1278,17 @@ def test_debug_mode_changes_log_level(): test_app.debug = True assert test_app.debug is True assert test_app.log.getEffectiveLevel() == logging.DEBUG + + +def test_raw_body_is_none_if_body_is_none(): + misc_kwargs = { + 'query_params': {}, + 'headers': {}, + 'uri_params': {}, + 'method': 'GET', + 'context': {}, + 'stage_vars': {}, + 'is_base64_encoded': False, + } + request = app.Request(body=None, **misc_kwargs) + assert request.raw_body is None diff --git a/tests/unit/test_local.py b/tests/unit/test_local.py index d28afb2a4..5e97cc8c4 100644 --- a/tests/unit/test_local.py +++ b/tests/unit/test_local.py @@ -347,7 +347,7 @@ def test_can_create_lambda_event(): 'headers': {'content-type': 'application/json'}, 'pathParameters': {'capture': 'other'}, 'queryStringParameters': {}, - 'body': '{}', + 'body': None, 'stageVariables': {}, }