From d729e4c6012c57ea626ca0b58126620afc121bf5 Mon Sep 17 00:00:00 2001 From: Arthur Darcet Date: Tue, 13 Dec 2016 12:40:45 +0100 Subject: [PATCH] Changing the status code of a response after it has been prepared raises a RuntimeError --- CHANGES.rst | 3 +++ aiohttp/web_reqrep.py | 6 +++++- tests/test_web_response.py | 8 ++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 4d1f6d55b8..0e1cbe7937 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -52,6 +52,9 @@ CHANGES - Avoid a race when application might start accepting incoming requests but startup signals are not processed yet e98e8c6 +- Raise a `RuntimeError` when trying to change the status of the HTTP response + after the headers have been sent + - - Fix bug with https proxy acquired cleanup #1340 diff --git a/aiohttp/web_reqrep.py b/aiohttp/web_reqrep.py index c91aa898db..d9fce3e2b1 100644 --- a/aiohttp/web_reqrep.py +++ b/aiohttp/web_reqrep.py @@ -509,7 +509,6 @@ def __init__(self, *, status=200, reason=None, headers=None): self._compression_force = False self._headers = CIMultiDict() self._cookies = SimpleCookie() - self.set_status(status, reason) self._req = None self._resp_impl = None @@ -522,6 +521,8 @@ def __init__(self, *, status=200, reason=None, headers=None): self._headers.extend(headers) self._headers.setdefault(hdrs.CONTENT_TYPE, 'application/octet-stream') + self.set_status(status, reason) + @property def prepared(self): return self._resp_impl is not None @@ -552,6 +553,9 @@ def reason(self): return self._reason def set_status(self, status, reason=None): + if self.prepared: + raise RuntimeError("Cannot change the response status code after " + "the headers have been sent") self._status = int(status) if reason is None: reason = ResponseImpl.calc_reason(status) diff --git a/tests/test_web_response.py b/tests/test_web_response.py index 5bdf270dd6..b044e31f1a 100644 --- a/tests/test_web_response.py +++ b/tests/test_web_response.py @@ -905,6 +905,14 @@ def test_drain_before_start(): yield from resp.drain() +@asyncio.coroutine +def test_changing_status_after_prepare_raises(): + resp = StreamResponse() + yield from resp.prepare(make_request('GET', '/')) + with pytest.raises(RuntimeError): + resp.set_status(400) + + def test_nonstr_text_in_ctor(): with pytest.raises(TypeError): Response(text=b'data')