From 25a3a80b0cf276450115b281115427923aeebc1e Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Mon, 7 May 2018 13:31:02 +0300 Subject: [PATCH 1/4] Disable reading response BODY after release --- aiohttp/client.py | 6 ++++-- aiohttp/client_reqrep.py | 12 +++++++++--- tests/test_client_functional.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/aiohttp/client.py b/aiohttp/client.py index 2e5d4ee38cd..45f2737e33a 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -362,8 +362,6 @@ async def _request(self, method, url, *, resp.close() raise TooManyRedirects( history[0].request_info, tuple(history)) - else: - resp.release() # For 301 and 302, mimic IE, now changed in RFC # https://github.com/kennethreitz/requests/pull/269 @@ -381,6 +379,10 @@ async def _request(self, method, url, *, if r_url is None: # see github.com/aio-libs/aiohttp/issues/2022 break + else: + # reading from correct redirection + # response is forbidden + resp.release() try: r_url = URL( diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index f4688cc300e..070c2cfdf1f 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -577,6 +577,7 @@ class ClientResponse(HeadersMixin): # setted up by ClientRequest after ClientResponse object creation # post-init stage allows to not change ctor signature _closed = True # to allow __del__ for non-initialized properly response + _released = False def __init__(self, method, url, *, writer, continue100, timer, @@ -795,6 +796,8 @@ def closed(self): return self._closed def close(self): + if not self._released: + self._notify_content() if self._closed: return @@ -806,9 +809,10 @@ def close(self): self._connection.close() self._connection = None self._cleanup_writer() - self._notify_content() def release(self): + if not self._released: + self._notify_content() if self._closed: return noop() @@ -818,7 +822,6 @@ def release(self): self._connection = None self._cleanup_writer() - self._notify_content() return noop() def raise_for_status(self): @@ -838,9 +841,10 @@ def _cleanup_writer(self): def _notify_content(self): content = self.content - if content and content.exception() is None and not content.is_eof(): + if content and content.exception() is None: content.set_exception( ClientConnectionError('Connection closed')) + self._released = True async def wait_for_close(self): if self._writer is not None: @@ -860,6 +864,8 @@ async def read(self): except BaseException: self.close() raise + elif self._released: + raise ClientConnectionError('Connection closed') return self._body diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 4e5f89e4602..becdd3c1aff 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -2549,3 +2549,35 @@ async def gen(): resp = await client.post('/', data=gen()) assert resp.status == 200 + + +async def test_read_from_closed_response(aiohttp_client): + async def handler(request): + return web.Response(body=b'data') + + app = web.Application() + app.add_routes([web.get('/', handler)]) + + client = await aiohttp_client(app) + + async with client.get('/') as resp: + assert resp.status == 200 + + with pytest.raises(aiohttp.ClientConnectionError): + await resp.read() + + +async def test_read_from_closed_content(aiohttp_client): + async def handler(request): + return web.Response(body=b'data') + + app = web.Application() + app.add_routes([web.get('/', handler)]) + + client = await aiohttp_client(app) + + async with client.get('/') as resp: + assert resp.status == 200 + + with pytest.raises(aiohttp.ClientConnectionError): + await resp.content.readline() From c653ca9ea707d5a6f28c44a10d733bd1a8622cf5 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Mon, 7 May 2018 13:34:42 +0300 Subject: [PATCH 2/4] Add changelog --- CHANGES/2983.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 CHANGES/2983.feature diff --git a/CHANGES/2983.feature b/CHANGES/2983.feature new file mode 100644 index 00000000000..9462a9be833 --- /dev/null +++ b/CHANGES/2983.feature @@ -0,0 +1 @@ +Disable reading response BODY after release \ No newline at end of file From 6214e85f33fecca8164fb2be7bbfa626880c5762 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Mon, 7 May 2018 16:18:11 +0300 Subject: [PATCH 3/4] Better coverage --- tests/test_client_functional.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index becdd3c1aff..7fc8af3c430 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -2567,6 +2567,23 @@ async def handler(request): await resp.read() +async def test_read_from_closed_response2(aiohttp_client): + async def handler(request): + return web.Response(body=b'data') + + app = web.Application() + app.add_routes([web.get('/', handler)]) + + client = await aiohttp_client(app) + + async with client.get('/') as resp: + assert resp.status == 200 + await resp.read() + + with pytest.raises(aiohttp.ClientConnectionError): + await resp.read() + + async def test_read_from_closed_content(aiohttp_client): async def handler(request): return web.Response(body=b'data') From 274d0e1a2be9aeccc8d2ce91e03d6bff13d7914f Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Mon, 7 May 2018 16:38:43 +0300 Subject: [PATCH 4/4] Change wording --- CHANGES/2983.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES/2983.feature b/CHANGES/2983.feature index 9462a9be833..5e5b60abe1d 100644 --- a/CHANGES/2983.feature +++ b/CHANGES/2983.feature @@ -1 +1 @@ -Disable reading response BODY after release \ No newline at end of file +Forbid reading response BODY after release \ No newline at end of file