summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorwrwrwr <git@wr.waw.pl>2014-10-17 15:46:42 +0200
committerTim Graham <timograham@gmail.com>2014-11-25 10:12:28 -0500
commit056a3c6c374f15e23746ea8568cd5b11bfe7d442 (patch)
tree528710827a03490d2fa4d3d630c5ab31828905a5 /django
parenta973fb2d68f2e23e599a601fdaec13d129207e9f (diff)
Fixed #23682 -- Enhanced circular redirects detection in tests.
When the test client detects a redirect to a URL seen in the currently followed chain it will now raise a RedirectCycleError instead of just returning the first repeated response. It will also complain when a single chain of redirects is longer than 20, as this often means a redirect loop with varying URLs, and even if it's not actually one, such long chains are likely to be treated as loops by browsers. Thanks Preston Timmons, Berker Peksag, and Tim Graham for reviews.
Diffstat (limited to 'django')
-rw-r--r--django/test/client.py31
1 files changed, 24 insertions, 7 deletions
diff --git a/django/test/client.py b/django/test/client.py
index fb8ff75e95..b2633bd7e7 100644
--- a/django/test/client.py
+++ b/django/test/client.py
@@ -27,7 +27,7 @@ from django.utils import six
from django.utils.six.moves.urllib.parse import urlparse, urlsplit
from django.test.utils import ContextList
-__all__ = ('Client', 'RequestFactory', 'encode_file', 'encode_multipart')
+__all__ = ('Client', 'RedirectCycleError', 'RequestFactory', 'encode_file', 'encode_multipart')
BOUNDARY = 'BoUnDaRyStRiNg'
@@ -35,6 +35,16 @@ MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY
CONTENT_TYPE_RE = re.compile('.*; charset=([\w\d-]+);?')
+class RedirectCycleError(Exception):
+ """
+ The test client has been asked to follow a redirect loop.
+ """
+ def __init__(self, message, last_response):
+ super(RedirectCycleError, self).__init__(message)
+ self.last_response = last_response
+ self.redirect_chain = last_response.redirect_chain
+
+
class FakePayload(object):
"""
A wrapper around BytesIO that restricts what can be read since data from
@@ -630,11 +640,11 @@ class Client(RequestFactory):
response.redirect_chain = []
while response.status_code in (301, 302, 303, 307):
- url = response.url
+ response_url = response.url
redirect_chain = response.redirect_chain
- redirect_chain.append((url, response.status_code))
+ redirect_chain.append((response_url, response.status_code))
- url = urlsplit(url)
+ url = urlsplit(response_url)
if url.scheme:
extra['wsgi.url_scheme'] = url.scheme
if url.hostname:
@@ -645,7 +655,14 @@ class Client(RequestFactory):
response = self.get(url.path, QueryDict(url.query), follow=False, **extra)
response.redirect_chain = redirect_chain
- # Prevent loops
- if response.redirect_chain[-1] in response.redirect_chain[0:-1]:
- break
+ if redirect_chain[-1] in redirect_chain[:-1]:
+ # Check that we're not redirecting to somewhere we've already
+ # been to, to prevent loops.
+ raise RedirectCycleError("Redirect loop detected.", last_response=response)
+ if len(redirect_chain) > 20:
+ # Such a lengthy chain likely also means a loop, but one with
+ # a growing path, changing view, or changing query argument;
+ # 20 is the value of "network.http.redirection-limit" from Firefox.
+ raise RedirectCycleError("Too many redirects.", last_response=response)
+
return response