diff options
-rw-r--r-- | requests/models.py | 23 | ||||
-rw-r--r-- | tests/test_lowlevel.py | 41 |
2 files changed, 57 insertions, 7 deletions
diff --git a/requests/models.py b/requests/models.py index a58af93f..6f6e7088 100644 --- a/requests/models.py +++ b/requests/models.py @@ -640,7 +640,9 @@ class Response(object): #: is a response. self.request = None - self.error = None + #: If there was an error in the processing of content, + #: then save the error that would return the same error when you re-appeal. + self._error = None def __enter__(self): return self @@ -751,15 +753,21 @@ class Response(object): try: for chunk in self.raw.stream(chunk_size, decode_content=True): yield chunk + except ProtocolError as e: - self._error = e - raise ChunkedEncodingError(e) + self._error = ChunkedEncodingError(e) + except DecodeError as e: - self._error = e - raise ContentDecodingError(e) + self._error = ContentDecodingError(e) + except ReadTimeoutError as e: - self._error = e - raise ConnectionError(e) + self._error = ConnectionError(e) + + finally: + # if we had an error - throw the saved error + if self._error: + raise self._error + else: # Standard file-like object. while True: @@ -832,6 +840,7 @@ class Response(object): else: self._content = b''.join(self.iter_content(CONTENT_CHUNK_SIZE)) or b'' + # if we had an error - throw the saved error if self._error is not None: raise self._error diff --git a/tests/test_lowlevel.py b/tests/test_lowlevel.py index 82c3b25a..59c43220 100644 --- a/tests/test_lowlevel.py +++ b/tests/test_lowlevel.py @@ -3,6 +3,7 @@ import pytest import threading import requests +from requests.exceptions import ChunkedEncodingError from tests.testserver.server import Server, consume_socket_content @@ -307,3 +308,43 @@ def test_fragment_update_on_redirect(): assert r.url == 'http://{}:{}/final-url/#relevant-section'.format(host, port) close_server.set() + + +def test_response_content_retains_error(): + """Verify that accessing response.content retains an error. + + See https://github.com/kennethreitz/requests/issues/4965 + """ + + data = "Some random stuff to read from remove server.\n" + + def response_handler(sock): + req = consume_socket_content(sock, timeout=0.5) + + # Send invalid chunked data (length mismatch) + sock.send( + b'HTTP/1.1 200 OK\r\n' + b'Transfer-Encoding: chunked\r\n' + b'\r\n2\r\n42\r\n8\r\n123\r\n' # 5 bytes missing + ) + + close_server = threading.Event() + server = Server(response_handler, wait_to_close_event=close_server) + + with server as (host, port): + url = 'http://{}:{}/path'.format(host, port) + r = requests.post(url, stream=True) + with pytest.raises(ChunkedEncodingError): + r.content + + # Access the bad response data again, I would expect the same + # error again. + + try: + content = r.content + except ChunkedEncodingError: + pass # fine, same exception + else: + assert False, "error response has content: {0!r}".format(content) + close_server.set() + |