From 3299771e3497199adda6ad6354f78742806cd320 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Thu, 7 Feb 2013 21:08:20 -0500 Subject: [PATCH 1/4] Fix the potential issue mentioned in #1151 See: https://github.com/kennethreitz/requests/pull/1151#issuecomment-12905796 This is solved by just reusing the PreparedRequest from the last request. --- requests/sessions.py | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/requests/sessions.py b/requests/sessions.py index 9dff2552..8c1152cd 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -74,9 +74,8 @@ def merge_kwargs(local_kwarg, default_kwarg): class SessionRedirectMixin(object): - - def resolve_redirects(self, resp, req, stream=False, timeout=None, - verify=True, cert=None, proxies=None): + def resolve_redirects(self, resp, prepared_request, stream=False, + timeout=None, verify=True, cert=None, proxies=None): """Receives a Response. Returns a generator of Responses.""" i = 0 @@ -93,7 +92,7 @@ class SessionRedirectMixin(object): resp.close() url = resp.headers['location'] - method = req.method + method = prepared_request.method # Handle redirection without scheme (see: RFC 1808 Section 4) if url.startswith('//'): @@ -106,34 +105,35 @@ class SessionRedirectMixin(object): # Compliant with RFC3986, we percent encode the url. url = urljoin(resp.url, requote_uri(url)) + prepared_request.url = url + # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4 - if resp.status_code == codes.see_other and req.method != 'HEAD': + if (resp.status_code == codes.see_other and + prepared_request.method != 'HEAD'): method = 'GET' # Do what the browsers do, despite standards... - if resp.status_code in (codes.moved, codes.found) and req.method == 'POST': + if (resp.status_code in (codes.moved, codes.found) and + prepared_request.method == 'POST'): method = 'GET' + prepared_request.method = method + # Remove the cookie headers that were sent. - headers = req.headers + headers = prepared_request.headers try: del headers['Cookie'] except KeyError: pass - resp = self.request( - url=url, - method=method, - headers=headers, - auth=req.auth, - cookies=req.cookies, - allow_redirects=False, + resp = self.send( + prepared_request, stream=stream, timeout=timeout, verify=verify, cert=cert, proxies=proxies, - hooks=req.hooks, + allow_redirects=False, ) i += 1 @@ -392,8 +392,9 @@ class Session(SessionRedirectMixin): r.elapsed = datetime.utcnow() - start # Redirect resolving generator. - gen = self.resolve_redirects(r, req, stream=stream, timeout=timeout, - verify=verify, cert=cert, proxies=proxies) + gen = self.resolve_redirects(r, request, stream=stream, + timeout=timeout, verify=verify, cert=cert, + proxies=proxies) # Resolve redirects if allowed. history = [resp for resp in gen] if allow_redirects else [] From e2ad0d0fe8f4bff1640417f67e7e321f030c1bed Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Thu, 7 Feb 2013 22:42:58 -0500 Subject: [PATCH 2/4] We shouldn't be sending the data on redirect. As such, we should remove the body from the old request as well as the Content-Length header. --- requests/sessions.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/requests/sessions.py b/requests/sessions.py index 8c1152cd..7ee62214 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -121,10 +121,13 @@ class SessionRedirectMixin(object): # Remove the cookie headers that were sent. headers = prepared_request.headers - try: - del headers['Cookie'] - except KeyError: - pass + for h in ('Cookie', 'Content-Length'): + try: + del headers[h] + except KeyError: + pass + + prepared_request.body = None resp = self.send( prepared_request, From e7bc9bf1b2c8abb51b56981548632baed3b4fb87 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Thu, 7 Feb 2013 22:53:37 -0500 Subject: [PATCH 3/4] Preserve the original request. Let's make a copy to preserve it. --- requests/sessions.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/requests/sessions.py b/requests/sessions.py index 7ee62214..01534c94 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -13,7 +13,7 @@ from datetime import datetime from .compat import cookielib from .cookies import cookiejar_from_dict -from .models import Request +from .models import Request, PreparedRequest from .hooks import default_hooks, dispatch_hook from .utils import from_key_val_list, default_headers from .exceptions import TooManyRedirects, InvalidSchema @@ -74,11 +74,17 @@ def merge_kwargs(local_kwarg, default_kwarg): class SessionRedirectMixin(object): - def resolve_redirects(self, resp, prepared_request, stream=False, - timeout=None, verify=True, cert=None, proxies=None): + def resolve_redirects(self, resp, req, stream=False, timeout=None, + verify=True, cert=None, proxies=None): """Receives a Response. Returns a generator of Responses.""" i = 0 + prepared_request = PreparedRequest() + prepared_request.body = req.body + prepared_request.headers = req.headers.copy() + prepared_request.hooks = req.hooks + prepared_request.method = req.method + prepared_request.url = req.url # ((resp.status_code is codes.see_other)) while (('location' in resp.headers and resp.status_code in REDIRECT_STATI)): From 9c8660dbb6e5d9b8694447e91e7936161f868ab0 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Sun, 10 Feb 2013 17:14:45 -0500 Subject: [PATCH 4/4] Resolve @piotr-dobrogost's concerns Piotr had good objections to my not re-sending the body of the request on 307. --- requests/sessions.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/requests/sessions.py b/requests/sessions.py index 01534c94..406d78eb 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -125,15 +125,16 @@ class SessionRedirectMixin(object): prepared_request.method = method - # Remove the cookie headers that were sent. - headers = prepared_request.headers - for h in ('Cookie', 'Content-Length'): - try: - del headers[h] - except KeyError: - pass + if resp.status_code is not codes.temporary: + if 'Content-Length' in prepared_request.headers: + del prepared_request.headers['Content-Length'] - prepared_request.body = None + prepared_request.body = None + + try: + del prepared_request.headers['Cookie'] + except KeyError: + pass resp = self.send( prepared_request,