From fe147914a644cabe11648fb313b990730ae25744 Mon Sep 17 00:00:00 2001 From: Jamie Lennox Date: Thu, 25 Aug 2016 10:34:49 +1000 Subject: [PATCH] Return b'' when reading closed response. A HTTPResponse returns a b'' when you read from a closed socket. BytesIO raises a ValueError. This only comes to light when you are specifically reading past the end of the stream in a chunking scenario like swiftclient does. Change-Id: I67ed45252deac9472cfb011a5eb89130a3791d6b Closes-Bug: #1616690 --- requests_mock/response.py | 20 ++++++++++++++++++-- requests_mock/tests/test_adapter.py | 12 ++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/requests_mock/response.py b/requests_mock/response.py index 54841fa..7d0ba20 100644 --- a/requests_mock/response.py +++ b/requests_mock/response.py @@ -103,6 +103,22 @@ def _extract_cookies(request, response, cookies): merge_cookies(response.cookies, cookies) +class _IOReader(six.BytesIO): + """A reader that makes a BytesIO look like a HTTPResponse. + + A HTTPResponse will return an empty string when you read from it after + the socket has been closed. A BytesIO will raise a ValueError. For + compatibility we want to do the same thing a HTTPResponse does. + """ + + def read(self, *args, **kwargs): + if self.closed: + return six.b('') + + # not a new style object in python 2 + return six.BytesIO.read(self, *args, **kwargs) + + def create_response(request, **kwargs): """ :param int status_code: The status code to return upon a successful @@ -142,12 +158,12 @@ def create_response(request, **kwargs): encoding = 'utf-8' content = text.encode(encoding) if content is not None: - body = six.BytesIO(content) + body = _IOReader(content) if not raw: raw = HTTPResponse(status=kwargs.get('status_code', _DEFAULT_STATUS), headers=kwargs.get('headers', {}), reason=kwargs.get('reason'), - body=body or six.BytesIO(six.b('')), + body=body or _IOReader(six.b('')), decode_content=False, preload_content=False, original_response=compat._fake_http_response) diff --git a/requests_mock/tests/test_adapter.py b/requests_mock/tests/test_adapter.py index d2ed929..b22fda0 100644 --- a/requests_mock/tests/test_adapter.py +++ b/requests_mock/tests/test_adapter.py @@ -664,3 +664,15 @@ class SessionAdapterTests(base.TestCase): self.assertEqual(data, resp.text) self.assertEqual(proxies, self.adapter.last_request.proxies) self.assertIsNot(proxies, self.adapter.last_request.proxies) + + def test_reading_closed_fp(self): + self.adapter.register_uri('GET', self.url, text='abc') + resp = self.session.get(self.url) + + # raw will have been closed during the request reading + self.assertTrue(resp.raw.closed) + + data = resp.raw.read() + + self.assertIsInstance(data, six.binary_type) + self.assertEqual(0, len(data))