diff --git a/requests/adapters.py b/requests/adapters.py index 12d23b01..eb7a2d28 100644 --- a/requests/adapters.py +++ b/requests/adapters.py @@ -385,9 +385,4 @@ class HTTPAdapter(BaseAdapter): else: raise - r = self.build_response(request, resp) - - if not stream: - r.content - - return r + return self.build_response(request, resp) diff --git a/requests/sessions.py b/requests/sessions.py index 6b21b5df..df85a25c 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -19,7 +19,8 @@ from .cookies import ( from .models import Request, PreparedRequest, DEFAULT_REDIRECT_LIMIT from .hooks import default_hooks, dispatch_hook from .utils import to_key_val_list, default_headers, to_native_string -from .exceptions import TooManyRedirects, InvalidSchema +from .exceptions import ( + TooManyRedirects, InvalidSchema, ChunkedEncodingError, ContentDecodingError) from .structures import CaseInsensitiveDict from .adapters import HTTPAdapter @@ -94,7 +95,10 @@ class SessionRedirectMixin(object): while resp.is_redirect: prepared_request = req.copy() - resp.content # Consume socket so it can be released + try: + resp.content # Consume socket so it can be released + except (ChunkedEncodingError, ContentDecodingError, RuntimeError): + resp.raw.read(decode_content=False) if i >= self.max_redirects: raise TooManyRedirects('Exceeded %s redirects.' % self.max_redirects) @@ -588,6 +592,9 @@ class Session(SessionRedirectMixin): r = history.pop() r.history = history + if not stream: + r.content + return r def get_adapter(self, url): diff --git a/test_requests.py b/test_requests.py index a1df1f19..a92b22c6 100755 --- a/test_requests.py +++ b/test_requests.py @@ -917,6 +917,45 @@ class RequestsTestCase(unittest.TestCase): assert h1 == h2 + def test_manual_redirect_with_partial_body_read(self): + s = requests.Session() + r1 = s.get(httpbin('redirect/2'), allow_redirects=False, stream=True) + assert r1.is_redirect + rg = s.resolve_redirects(r1, r1.request, stream=True) + + # read only the first eight bytes of the response body, + # then follow the redirect + r1.iter_content(8) + r2 = next(rg) + assert r2.is_redirect + + # read all of the response via iter_content, + # then follow the redirect + for _ in r2.iter_content(): + pass + r3 = next(rg) + assert not r3.is_redirect + + def _patch_adapter_gzipped_redirect(self, session, url): + adapter = session.get_adapter(url=url) + org_build_response = adapter.build_response + self._patched_response = False + + def build_response(*args, **kwargs): + resp = org_build_response(*args, **kwargs) + if not self._patched_response: + resp.raw.headers['content-encoding'] = 'gzip' + self._patched_response = True + return resp + + adapter.build_response = build_response + + def test_redirect_with_wrong_gzipped_header(self): + s = requests.Session() + url = httpbin('redirect/1') + self._patch_adapter_gzipped_redirect(s, url) + s.get(url) + class TestContentEncodingDetection(unittest.TestCase): @@ -1321,7 +1360,7 @@ def test_data_argument_accepts_tuples(list_of_tuples): hooks=default_hooks() ) assert p.body == urlencode(data) - + if __name__ == '__main__': unittest.main()