Browse Source

Fixed #23960 -- Removed http.fix_location_header

Thanks Carl Meyer for the report and Tim Graham for the review.
Claude Paroz 10 years ago
parent
commit
a0c2eb46dd

+ 0 - 1
django/core/handlers/base.py

@@ -23,7 +23,6 @@ logger = logging.getLogger('django.request')
 class BaseHandler(object):
     # Changes that are always applied to a response (in this order).
     response_fixes = [
-        http.fix_location_header,
         http.conditional_content_removal,
     ]
 

+ 3 - 3
django/http/__init__.py

@@ -8,7 +8,7 @@ from django.http.response import (
     HttpResponseNotFound, HttpResponseNotAllowed, HttpResponseGone,
     HttpResponseServerError, Http404, BadHeaderError, JsonResponse,
 )
-from django.http.utils import fix_location_header, conditional_content_removal
+from django.http.utils import conditional_content_removal
 
 __all__ = [
     'SimpleCookie', 'parse_cookie', 'HttpRequest', 'QueryDict',
@@ -17,6 +17,6 @@ __all__ = [
     'HttpResponsePermanentRedirect', 'HttpResponseNotModified',
     'HttpResponseBadRequest', 'HttpResponseForbidden', 'HttpResponseNotFound',
     'HttpResponseNotAllowed', 'HttpResponseGone', 'HttpResponseServerError',
-    'Http404', 'BadHeaderError', 'fix_location_header', 'JsonResponse',
-    'FileResponse', 'conditional_content_removal',
+    'Http404', 'BadHeaderError', 'JsonResponse', 'FileResponse',
+    'conditional_content_removal',
 ]

+ 0 - 13
django/http/utils.py

@@ -9,19 +9,6 @@ Functions that modify an HTTP request or response in some way.
 # universally applicable.
 
 
-def fix_location_header(request, response):
-    """
-    Ensures that we always use an absolute URI in any location header in the
-    response. This is required by RFC 2616, section 14.30.
-
-    Code constructing response objects is free to insert relative paths, as
-    this function converts them to absolute paths.
-    """
-    if 'Location' in response:
-        response['Location'] = request.build_absolute_uri(response['Location'])
-    return response
-
-
 def conditional_content_removal(request, response):
     """
     Removes the content of responses for HEAD requests, 1xx, 204 and 304

+ 1 - 1
django/middleware/common.py

@@ -86,7 +86,7 @@ class CommonMiddleware(object):
         if new_url == old_url:
             # No redirects required.
             return
-        if new_url[0]:
+        if new_url[0] != old_url[0]:
             newurl = "%s://%s%s" % (
                 request.scheme,
                 new_url[0], urlquote(new_url[1]))

+ 6 - 10
django/middleware/locale.py

@@ -40,16 +40,12 @@ class LocaleMiddleware(object):
 
             if path_valid:
                 script_prefix = get_script_prefix()
-                language_url = "%s://%s%s" % (
-                    request.scheme,
-                    request.get_host(),
-                    # insert language after the script prefix and before the
-                    # rest of the URL
-                    request.get_full_path().replace(
-                        script_prefix,
-                        '%s%s/' % (script_prefix, language),
-                        1
-                    )
+                # Insert language after the script prefix and before the
+                # rest of the URL
+                language_url = request.get_full_path().replace(
+                    script_prefix,
+                    '%s%s/' % (script_prefix, language),
+                    1
                 )
                 return self.response_redirect_class(language_url)
 

+ 21 - 7
django/test/testcases.py

@@ -37,7 +37,9 @@ from django.test.utils import (
     override_settings,
 )
 from django.utils import six
-from django.utils.deprecation import RemovedInDjango20Warning
+from django.utils.deprecation import (
+    RemovedInDjango20Warning, RemovedInDjango21Warning,
+)
 from django.utils.encoding import force_text
 from django.utils.six.moves.urllib.parse import (
     unquote, urlparse, urlsplit, urlunsplit,
@@ -249,11 +251,15 @@ class SimpleTestCase(unittest.TestCase):
         TestClient to do a request (use fetch_redirect_response=False to check
         such links without fetching them).
         """
+        if host is not None:
+            warnings.warn(
+                "The host argument is deprecated and no longer used by assertRedirects",
+                RemovedInDjango21Warning, stacklevel=2
+            )
+
         if msg_prefix:
             msg_prefix += ": "
 
-        e_scheme, e_netloc, e_path, e_query, e_fragment = urlsplit(expected_url)
-
         if hasattr(response, 'redirect_chain'):
             # The request was a followed redirect
             self.assertTrue(len(response.redirect_chain) > 0,
@@ -295,10 +301,18 @@ class SimpleTestCase(unittest.TestCase):
                     " response code was %d (expected %d)" %
                         (path, redirect_response.status_code, target_status_code))
 
-        e_scheme = e_scheme if e_scheme else scheme or 'http'
-        e_netloc = e_netloc if e_netloc else host or 'testserver'
-        expected_url = urlunsplit((e_scheme, e_netloc, e_path, e_query,
-            e_fragment))
+        if url != expected_url:
+            # For temporary backwards compatibility, try to compare with a relative url
+            e_scheme, e_netloc, e_path, e_query, e_fragment = urlsplit(expected_url)
+            relative_url = urlunsplit(('', '', e_path, e_query, e_fragment))
+            if url == relative_url:
+                warnings.warn(
+                    "assertRedirects had to strip the scheme and domain from the "
+                    "expected URL, as it was always added automatically to URLs "
+                    "before Django 1.9. Please update your expected URLs by "
+                    "removing the scheme and domain.",
+                    RemovedInDjango21Warning, stacklevel=2)
+                expected_url = relative_url
 
         self.assertEqual(url, expected_url,
             msg_prefix + "Response redirected to '%s', expected '%s'" %

+ 4 - 0
docs/internals/deprecation.txt

@@ -22,6 +22,10 @@ details on these changes.
 
 * The ``assignment_tag`` helper will be removed.
 
+* The ``host`` argument to ``assertsRedirects`` will be removed. The
+  compatibility layer which allows absolute URLs to be considered equal to
+  relative ones when the path is identical will also be removed.
+
 .. _deprecation-removed-in-2.0:
 
 2.0

+ 4 - 2
docs/ref/request-response.txt

@@ -824,8 +824,10 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in
 
     The first argument to the constructor is required -- the path to redirect
     to. This can be a fully qualified URL
-    (e.g. ``'http://www.yahoo.com/search/'``) or an absolute path with no
-    domain (e.g. ``'/search/'``). See :class:`HttpResponse` for other optional
+    (e.g. ``'http://www.yahoo.com/search/'``), an absolute path with no domain
+    (e.g. ``'/search/'``), or even a relative path (e.g. ``'search/'``). In that
+    last case, the client browser will reconstruct the full URL itself
+    according to the current path. See :class:`HttpResponse` for other optional
     constructor arguments. Note that this returns an HTTP status code 302.
 
     .. attribute:: HttpResponseRedirect.url

+ 15 - 0
docs/releases/1.9.txt

@@ -266,6 +266,21 @@ a directory. Now, Django only silences the exception if the template source
 does not exist. All other situations result in the original ``IOError`` being
 raised.
 
+HTTP redirects no longer forced to absolute URIs
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Relative redirects are no longer converted to absolute URIs. :rfc:`2616`
+required the ``Location`` header in redirect responses to be an absolute URI,
+but it has been superseded by :rfc:`7231` which allows relative URIs in
+``Location``, recognizing the actual practice of user agents, almost all of
+which support them.
+
+Consequently, the expected URLs passed to ``assertRedirects`` should generally
+no longer include the scheme and domain part of the URLs. For example,
+``self.assertRedirects(response, 'http://testserver/some-url/')`` should be
+replaced by ``self.assertRedirects(response, '/some-url/')`` (unless the
+redirection specifically contained an absolute URL, of course).
+
 Miscellaneous
 ~~~~~~~~~~~~~
 

+ 6 - 9
docs/topics/testing/tools.txt

@@ -1398,7 +1398,7 @@ your test suite.
     You can use this as a context manager in the same way as
     :meth:`~SimpleTestCase.assertTemplateUsed`.
 
-.. method:: SimpleTestCase.assertRedirects(response, expected_url, status_code=302, target_status_code=200, host=None, msg_prefix='', fetch_redirect_response=True)
+.. method:: SimpleTestCase.assertRedirects(response, expected_url, status_code=302, target_status_code=200, msg_prefix='', fetch_redirect_response=True)
 
     Asserts that the response returned a ``status_code`` redirect status,
     redirected to ``expected_url`` (including any ``GET`` data), and that the
@@ -1408,14 +1408,6 @@ your test suite.
     ``target_status_code`` will be the url and status code for the final
     point of the redirect chain.
 
-    The ``host`` argument sets a default host if ``expected_url`` doesn't
-    include one (e.g. ``"/bar/"``).  If ``expected_url`` is an absolute URL that
-    includes a host (e.g. ``"http://testhost/bar/"``), the ``host`` parameter
-    will be ignored. Note that the test client doesn't support fetching external
-    URLs, but the parameter may be useful if you are testing with a custom HTTP
-    host (for example, initializing the test client with
-    ``Client(HTTP_HOST="testhost")``.
-
     If ``fetch_redirect_response`` is ``False``, the final page won't be
     loaded. Since the test client can't fetch externals URLs, this is
     particularly useful if ``expected_url`` isn't part of your Django app.
@@ -1425,6 +1417,11 @@ your test suite.
     the original request's scheme is used. If present, the scheme in
     ``expected_url`` is the one used to make the comparisons to.
 
+    .. deprecated:: 1.9
+
+        The ``host`` argument is deprecated, as redirections are no longer
+        forced to be absolute URLs.
+
 .. method:: SimpleTestCase.assertHTMLEqual(html1, html2, msg=None)
 
     Asserts that the strings ``html1`` and ``html2`` are equal. The comparison

+ 20 - 20
tests/generic_views/test_edit.py

@@ -87,7 +87,7 @@ class BasicFormTests(TestCase):
 
     def test_post_data(self):
         res = self.client.post('/contact/', {'name': "Me", 'message': "Hello"})
-        self.assertRedirects(res, 'http://testserver/list/authors/')
+        self.assertRedirects(res, '/list/authors/')
 
 
 class ModelFormMixinTests(TestCase):
@@ -117,7 +117,7 @@ class CreateViewTests(TestCase):
         res = self.client.post('/edit/authors/create/',
                         {'name': 'Randall Munroe', 'slug': 'randall-munroe'})
         self.assertEqual(res.status_code, 302)
-        self.assertRedirects(res, 'http://testserver/list/authors/')
+        self.assertRedirects(res, '/list/authors/')
         self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe>'])
 
     def test_create_invalid(self):
@@ -133,14 +133,14 @@ class CreateViewTests(TestCase):
                         {'name': 'Rene Magritte'})
         self.assertEqual(res.status_code, 302)
         artist = Artist.objects.get(name='Rene Magritte')
-        self.assertRedirects(res, 'http://testserver/detail/artist/%d/' % artist.pk)
+        self.assertRedirects(res, '/detail/artist/%d/' % artist.pk)
         self.assertQuerysetEqual(Artist.objects.all(), ['<Artist: Rene Magritte>'])
 
     def test_create_with_redirect(self):
         res = self.client.post('/edit/authors/create/redirect/',
                             {'name': 'Randall Munroe', 'slug': 'randall-munroe'})
         self.assertEqual(res.status_code, 302)
-        self.assertRedirects(res, 'http://testserver/edit/authors/create/')
+        self.assertRedirects(res, '/edit/authors/create/')
         self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe>'])
 
     @ignore_warnings(category=RemovedInDjango20Warning)
@@ -152,7 +152,7 @@ class CreateViewTests(TestCase):
         self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe>'])
         self.assertEqual(res.status_code, 302)
         pk = Author.objects.first().pk
-        self.assertRedirects(res, 'http://testserver/edit/author/%d/update/' % pk)
+        self.assertRedirects(res, '/edit/author/%d/update/' % pk)
         # Also test with escaped chars in URL
         res = self.client.post(
             '/edit/authors/create/interpolate_redirect_nonascii/',
@@ -160,7 +160,7 @@ class CreateViewTests(TestCase):
         )
         self.assertEqual(res.status_code, 302)
         pk = Author.objects.get(name='John Doe').pk
-        self.assertRedirects(res, 'http://testserver/%C3%A9dit/author/{}/update/'.format(pk))
+        self.assertRedirects(res, '/%C3%A9dit/author/{}/update/'.format(pk))
 
     def test_create_with_special_properties(self):
         res = self.client.get('/edit/authors/create/special/')
@@ -189,7 +189,7 @@ class CreateViewTests(TestCase):
         res = self.client.post('/edit/authors/create/restricted/',
             {'name': 'Randall Munroe', 'slug': 'randall-munroe'})
         self.assertEqual(res.status_code, 302)
-        self.assertRedirects(res, 'http://testserver/accounts/login/?next=/edit/authors/create/restricted/')
+        self.assertRedirects(res, '/accounts/login/?next=/edit/authors/create/restricted/')
 
     def test_create_view_with_restricted_fields(self):
 
@@ -249,7 +249,7 @@ class UpdateViewTests(TestCase):
         res = self.client.post('/edit/author/%d/update/' % a.pk,
                         {'name': 'Randall Munroe (xkcd)', 'slug': 'randall-munroe'})
         self.assertEqual(res.status_code, 302)
-        self.assertRedirects(res, 'http://testserver/list/authors/')
+        self.assertRedirects(res, '/list/authors/')
         self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe (xkcd)>'])
 
     def test_update_invalid(self):
@@ -269,7 +269,7 @@ class UpdateViewTests(TestCase):
         res = self.client.post('/edit/artists/%d/update/' % a.pk,
                         {'name': 'Rene Magritte'})
         self.assertEqual(res.status_code, 302)
-        self.assertRedirects(res, 'http://testserver/detail/artist/%d/' % a.pk)
+        self.assertRedirects(res, '/detail/artist/%d/' % a.pk)
         self.assertQuerysetEqual(Artist.objects.all(), ['<Artist: Rene Magritte>'])
 
     def test_update_with_redirect(self):
@@ -280,7 +280,7 @@ class UpdateViewTests(TestCase):
         res = self.client.post('/edit/author/%d/update/redirect/' % a.pk,
                         {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'})
         self.assertEqual(res.status_code, 302)
-        self.assertRedirects(res, 'http://testserver/edit/authors/create/')
+        self.assertRedirects(res, '/edit/authors/create/')
         self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe (author of xkcd)>'])
 
     @ignore_warnings(category=RemovedInDjango20Warning)
@@ -296,7 +296,7 @@ class UpdateViewTests(TestCase):
         self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe (author of xkcd)>'])
         self.assertEqual(res.status_code, 302)
         pk = Author.objects.first().pk
-        self.assertRedirects(res, 'http://testserver/edit/author/%d/update/' % pk)
+        self.assertRedirects(res, '/edit/author/%d/update/' % pk)
         # Also test with escaped chars in URL
         res = self.client.post(
             '/edit/author/%d/update/interpolate_redirect_nonascii/' % a.pk,
@@ -304,7 +304,7 @@ class UpdateViewTests(TestCase):
         )
         self.assertEqual(res.status_code, 302)
         pk = Author.objects.get(name='John Doe').pk
-        self.assertRedirects(res, 'http://testserver/%C3%A9dit/author/{}/update/'.format(pk))
+        self.assertRedirects(res, '/%C3%A9dit/author/{}/update/'.format(pk))
 
     def test_update_with_special_properties(self):
         a = Author.objects.create(
@@ -322,7 +322,7 @@ class UpdateViewTests(TestCase):
         res = self.client.post('/edit/author/%d/update/special/' % a.pk,
                         {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'})
         self.assertEqual(res.status_code, 302)
-        self.assertRedirects(res, 'http://testserver/detail/author/%d/' % a.pk)
+        self.assertRedirects(res, '/detail/author/%d/' % a.pk)
         self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe (author of xkcd)>'])
 
     def test_update_without_redirect(self):
@@ -354,7 +354,7 @@ class UpdateViewTests(TestCase):
         res = self.client.post('/edit/author/update/',
                         {'name': 'Randall Munroe (xkcd)', 'slug': 'randall-munroe'})
         self.assertEqual(res.status_code, 302)
-        self.assertRedirects(res, 'http://testserver/list/authors/')
+        self.assertRedirects(res, '/list/authors/')
         self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe (xkcd)>'])
 
 
@@ -372,7 +372,7 @@ class DeleteViewTests(TestCase):
         # Deletion with POST
         res = self.client.post('/edit/author/%d/delete/' % a.pk)
         self.assertEqual(res.status_code, 302)
-        self.assertRedirects(res, 'http://testserver/list/authors/')
+        self.assertRedirects(res, '/list/authors/')
         self.assertQuerysetEqual(Author.objects.all(), [])
 
     def test_delete_by_delete(self):
@@ -380,14 +380,14 @@ class DeleteViewTests(TestCase):
         a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'})
         res = self.client.delete('/edit/author/%d/delete/' % a.pk)
         self.assertEqual(res.status_code, 302)
-        self.assertRedirects(res, 'http://testserver/list/authors/')
+        self.assertRedirects(res, '/list/authors/')
         self.assertQuerysetEqual(Author.objects.all(), [])
 
     def test_delete_with_redirect(self):
         a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'})
         res = self.client.post('/edit/author/%d/delete/redirect/' % a.pk)
         self.assertEqual(res.status_code, 302)
-        self.assertRedirects(res, 'http://testserver/edit/authors/create/')
+        self.assertRedirects(res, '/edit/authors/create/')
         self.assertQuerysetEqual(Author.objects.all(), [])
 
     @ignore_warnings(category=RemovedInDjango20Warning)
@@ -395,13 +395,13 @@ class DeleteViewTests(TestCase):
         a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'})
         res = self.client.post('/edit/author/%d/delete/interpolate_redirect/' % a.pk)
         self.assertEqual(res.status_code, 302)
-        self.assertRedirects(res, 'http://testserver/edit/authors/create/?deleted=%d' % a.pk)
+        self.assertRedirects(res, '/edit/authors/create/?deleted=%d' % a.pk)
         self.assertQuerysetEqual(Author.objects.all(), [])
         # Also test with escaped chars in URL
         a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'})
         res = self.client.post('/edit/author/{}/delete/interpolate_redirect_nonascii/'.format(a.pk))
         self.assertEqual(res.status_code, 302)
-        self.assertRedirects(res, 'http://testserver/%C3%A9dit/authors/create/?deleted={}'.format(a.pk))
+        self.assertRedirects(res, '/%C3%A9dit/authors/create/?deleted={}'.format(a.pk))
 
     def test_delete_with_special_properties(self):
         a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'})
@@ -414,7 +414,7 @@ class DeleteViewTests(TestCase):
 
         res = self.client.post('/edit/author/%d/delete/special/' % a.pk)
         self.assertEqual(res.status_code, 302)
-        self.assertRedirects(res, 'http://testserver/list/authors/')
+        self.assertRedirects(res, '/list/authors/')
         self.assertQuerysetEqual(Author.objects.all(), [])
 
     def test_delete_without_redirect(self):

+ 2 - 16
tests/http_utils/tests.py

@@ -3,10 +3,8 @@ from __future__ import unicode_literals
 import gzip
 import io
 
-from django.http import (
-    HttpRequest, HttpResponse, HttpResponseRedirect, StreamingHttpResponse,
-)
-from django.http.utils import conditional_content_removal, fix_location_header
+from django.http import HttpRequest, HttpResponse, StreamingHttpResponse
+from django.http.utils import conditional_content_removal
 from django.test import TestCase
 
 
@@ -71,15 +69,3 @@ class HttpUtilTests(TestCase):
         res = StreamingHttpResponse(['abc'])
         conditional_content_removal(req, res)
         self.assertEqual(b''.join(res), b'')
-
-    def test_fix_location_without_get_host(self):
-        """
-        Tests that you can return an absolute redirect when the request
-        host is not in ALLOWED_HOSTS. Issue #20472
-        """
-        request = HttpRequest()
-
-        def bomb():
-            self.assertTrue(False)
-        request.get_host = bomb
-        fix_location_header(request, HttpResponseRedirect('http://example.com'))

+ 1 - 1
tests/i18n/patterns/tests.py

@@ -248,7 +248,7 @@ class URLRedirectWithoutTrailingSlashTests(URLTestCaseBase):
     def test_en_redirect(self):
         response = self.client.get('/account/register', HTTP_ACCEPT_LANGUAGE='en', follow=True)
         # target status code of 301 because of CommonMiddleware redirecting
-        self.assertIn(('http://testserver/en/account/register/', 301), response.redirect_chain)
+        self.assertIn(('/en/account/register/', 301), response.redirect_chain)
         self.assertRedirects(response, '/en/account/register/', 302)
 
         response = self.client.get('/prefixed.xml', HTTP_ACCEPT_LANGUAGE='en', follow=True)

+ 6 - 6
tests/middleware/tests.py

@@ -64,7 +64,7 @@ class CommonMiddlewareTest(TestCase):
         request = self.rf.get('/slash')
         r = CommonMiddleware().process_request(request)
         self.assertEqual(r.status_code, 301)
-        self.assertEqual(r.url, 'http://testserver/slash/')
+        self.assertEqual(r.url, '/slash/')
 
     @override_settings(APPEND_SLASH=True, DEBUG=True)
     def test_append_slash_no_redirect_on_POST_in_DEBUG(self):
@@ -106,7 +106,7 @@ class CommonMiddlewareTest(TestCase):
         self.assertEqual(r.status_code, 301)
         self.assertEqual(
             r.url,
-            'http://testserver/needsquoting%23/')
+            '/needsquoting%23/')
 
     @override_settings(APPEND_SLASH=False, PREPEND_WWW=True)
     def test_prepend_www(self):
@@ -174,7 +174,7 @@ class CommonMiddlewareTest(TestCase):
         self.assertIsNotNone(r,
             "CommonMiddlware failed to return APPEND_SLASH redirect using request.urlconf")
         self.assertEqual(r.status_code, 301)
-        self.assertEqual(r.url, 'http://testserver/customurlconf/slash/')
+        self.assertEqual(r.url, '/customurlconf/slash/')
 
     @override_settings(APPEND_SLASH=True, DEBUG=True)
     def test_append_slash_no_redirect_on_POST_in_DEBUG_custom_urlconf(self):
@@ -212,7 +212,7 @@ class CommonMiddlewareTest(TestCase):
         self.assertEqual(r.status_code, 301)
         self.assertEqual(
             r.url,
-            'http://testserver/customurlconf/needsquoting%23/')
+            '/customurlconf/needsquoting%23/')
 
     @override_settings(APPEND_SLASH=False, PREPEND_WWW=True)
     def test_prepend_www_custom_urlconf(self):
@@ -264,7 +264,7 @@ class CommonMiddlewareTest(TestCase):
         request = self.rf.get('/slash')
         r = CommonMiddleware().process_request(request)
         self.assertEqual(r.status_code, 301)
-        self.assertEqual(r.url, 'http://testserver/slash/')
+        self.assertEqual(r.url, '/slash/')
         self.assertIsInstance(r, HttpResponsePermanentRedirect)
 
     def test_response_redirect_class_subclass(self):
@@ -274,7 +274,7 @@ class CommonMiddlewareTest(TestCase):
         request = self.rf.get('/slash')
         r = MyCommonMiddleware().process_request(request)
         self.assertEqual(r.status_code, 302)
-        self.assertEqual(r.url, 'http://testserver/slash/')
+        self.assertEqual(r.url, '/slash/')
         self.assertIsInstance(r, HttpResponseRedirect)
 
 

+ 14 - 27
tests/test_client/tests.py

@@ -176,40 +176,27 @@ class ClientTest(TestCase):
     def test_redirect(self):
         "GET a URL that redirects elsewhere"
         response = self.client.get('/redirect_view/')
-        # Check that the response was a 302 (redirect) and that
-        # assertRedirect() understands to put an implicit http://testserver/ in
-        # front of non-absolute URLs.
+        # Check that the response was a 302 (redirect)
         self.assertRedirects(response, '/get_view/')
 
-        host = 'django.testserver'
-        client_providing_host = Client(HTTP_HOST=host)
-        response = client_providing_host.get('/redirect_view/')
-        # Check that the response was a 302 (redirect) with absolute URI
-        self.assertRedirects(response, '/get_view/', host=host)
-
     def test_redirect_with_query(self):
         "GET a URL that redirects with given GET parameters"
         response = self.client.get('/redirect_view/', {'var': 'value'})
 
         # Check if parameters are intact
-        self.assertRedirects(response, 'http://testserver/get_view/?var=value')
+        self.assertRedirects(response, '/get_view/?var=value')
 
     def test_permanent_redirect(self):
         "GET a URL that redirects permanently elsewhere"
         response = self.client.get('/permanent_redirect_view/')
         # Check that the response was a 301 (permanent redirect)
-        self.assertRedirects(response, 'http://testserver/get_view/', status_code=301)
-
-        client_providing_host = Client(HTTP_HOST='django.testserver')
-        response = client_providing_host.get('/permanent_redirect_view/')
-        # Check that the response was a 301 (permanent redirect) with absolute URI
-        self.assertRedirects(response, 'http://django.testserver/get_view/', status_code=301)
+        self.assertRedirects(response, '/get_view/', status_code=301)
 
     def test_temporary_redirect(self):
         "GET a URL that does a non-permanent redirect"
         response = self.client.get('/temporary_redirect_view/')
         # Check that the response was a 302 (non-permanent redirect)
-        self.assertRedirects(response, 'http://testserver/get_view/', status_code=302)
+        self.assertRedirects(response, '/get_view/', status_code=302)
 
     def test_redirect_to_strange_location(self):
         "GET a URL that redirects to a non-200 page"
@@ -217,12 +204,12 @@ class ClientTest(TestCase):
 
         # Check that the response was a 302, and that
         # the attempt to get the redirection location returned 301 when retrieved
-        self.assertRedirects(response, 'http://testserver/permanent_redirect_view/', target_status_code=301)
+        self.assertRedirects(response, '/permanent_redirect_view/', target_status_code=301)
 
     def test_follow_redirect(self):
         "A URL that redirects can be followed to termination."
         response = self.client.get('/double_redirect_view/', follow=True)
-        self.assertRedirects(response, 'http://testserver/get_view/', status_code=302, target_status_code=200)
+        self.assertRedirects(response, '/get_view/', status_code=302, target_status_code=200)
         self.assertEqual(len(response.redirect_chain), 2)
 
     def test_redirect_http(self):
@@ -364,7 +351,7 @@ class ClientTest(TestCase):
 
         # Get the page without logging in. Should result in 302.
         response = self.client.get('/login_protected_view/')
-        self.assertRedirects(response, 'http://testserver/accounts/login/?next=/login_protected_view/')
+        self.assertRedirects(response, '/accounts/login/?next=/login_protected_view/')
 
         # Log in
         login = self.client.login(username='testclient', password='password')
@@ -380,7 +367,7 @@ class ClientTest(TestCase):
 
         # Get the page without logging in. Should result in 302.
         response = self.client.get('/login_protected_method_view/')
-        self.assertRedirects(response, 'http://testserver/accounts/login/?next=/login_protected_method_view/')
+        self.assertRedirects(response, '/accounts/login/?next=/login_protected_method_view/')
 
         # Log in
         login = self.client.login(username='testclient', password='password')
@@ -396,7 +383,7 @@ class ClientTest(TestCase):
 
         # Get the page without logging in. Should result in 302.
         response = self.client.get('/login_protected_view_custom_redirect/')
-        self.assertRedirects(response, 'http://testserver/accounts/login/?redirect_to=/login_protected_view_custom_redirect/')
+        self.assertRedirects(response, '/accounts/login/?redirect_to=/login_protected_view_custom_redirect/')
 
         # Log in
         login = self.client.login(username='testclient', password='password')
@@ -434,7 +421,7 @@ class ClientTest(TestCase):
 
         # Request a page that requires a login
         response = self.client.get('/login_protected_view/')
-        self.assertRedirects(response, 'http://testserver/accounts/login/?next=/login_protected_view/')
+        self.assertRedirects(response, '/accounts/login/?next=/login_protected_view/')
 
     @override_settings(SESSION_ENGINE="django.contrib.sessions.backends.signed_cookies")
     def test_logout_cookie_sessions(self):
@@ -445,7 +432,7 @@ class ClientTest(TestCase):
 
         # Get the page without logging in. Should result in 302.
         response = self.client.get('/permission_protected_view/')
-        self.assertRedirects(response, 'http://testserver/accounts/login/?next=/permission_protected_view/')
+        self.assertRedirects(response, '/accounts/login/?next=/permission_protected_view/')
 
         # Log in
         login = self.client.login(username='testclient', password='password')
@@ -453,7 +440,7 @@ class ClientTest(TestCase):
 
         # Log in with wrong permissions. Should result in 302.
         response = self.client.get('/permission_protected_view/')
-        self.assertRedirects(response, 'http://testserver/accounts/login/?next=/permission_protected_view/')
+        self.assertRedirects(response, '/accounts/login/?next=/permission_protected_view/')
 
         # TODO: Log in with right permissions and request the page again
 
@@ -477,7 +464,7 @@ class ClientTest(TestCase):
 
         # Get the page without logging in. Should result in 302.
         response = self.client.get('/permission_protected_method_view/')
-        self.assertRedirects(response, 'http://testserver/accounts/login/?next=/permission_protected_method_view/')
+        self.assertRedirects(response, '/accounts/login/?next=/permission_protected_method_view/')
 
         # Log in
         login = self.client.login(username='testclient', password='password')
@@ -485,7 +472,7 @@ class ClientTest(TestCase):
 
         # Log in with wrong permissions. Should result in 302.
         response = self.client.get('/permission_protected_method_view/')
-        self.assertRedirects(response, 'http://testserver/accounts/login/?next=/permission_protected_method_view/')
+        self.assertRedirects(response, '/accounts/login/?next=/permission_protected_method_view/')
 
         # TODO: Log in with right permissions and request the page again
 

+ 19 - 14
tests/test_client_regress/tests.py

@@ -18,7 +18,9 @@ from django.test import Client, TestCase, ignore_warnings, override_settings
 from django.test.client import RedirectCycleError, RequestFactory, encode_file
 from django.test.utils import ContextList, str_prefix
 from django.utils._os import upath
-from django.utils.deprecation import RemovedInDjango20Warning
+from django.utils.deprecation import (
+    RemovedInDjango20Warning, RemovedInDjango21Warning,
+)
 from django.utils.translation import ugettext_lazy
 
 from .models import CustomUser
@@ -342,12 +344,12 @@ class AssertRedirectsTests(TestCase):
         try:
             self.assertRedirects(response, '/get_view/')
         except AssertionError as e:
-            self.assertIn("Response redirected to 'http://testserver/get_view/?var=value', expected 'http://testserver/get_view/'", str(e))
+            self.assertIn("Response redirected to '/get_view/?var=value', expected '/get_view/'", str(e))
 
         try:
             self.assertRedirects(response, '/get_view/', msg_prefix='abc')
         except AssertionError as e:
-            self.assertIn("abc: Response redirected to 'http://testserver/get_view/?var=value', expected 'http://testserver/get_view/'", str(e))
+            self.assertIn("abc: Response redirected to '/get_view/?var=value', expected '/get_view/'", str(e))
 
     def test_incorrect_target(self):
         "An assertion is raised if the response redirects to another target"
@@ -380,7 +382,7 @@ class AssertRedirectsTests(TestCase):
             status_code=302, target_status_code=200)
 
         self.assertEqual(len(response.redirect_chain), 1)
-        self.assertEqual(response.redirect_chain[0], ('http://testserver/no_template_view/', 302))
+        self.assertEqual(response.redirect_chain[0], ('/no_template_view/', 302))
 
     def test_multiple_redirect_chain(self):
         "You can follow a redirect chain of multiple redirects"
@@ -389,9 +391,9 @@ class AssertRedirectsTests(TestCase):
             status_code=302, target_status_code=200)
 
         self.assertEqual(len(response.redirect_chain), 3)
-        self.assertEqual(response.redirect_chain[0], ('http://testserver/redirects/further/', 302))
-        self.assertEqual(response.redirect_chain[1], ('http://testserver/redirects/further/more/', 302))
-        self.assertEqual(response.redirect_chain[2], ('http://testserver/no_template_view/', 302))
+        self.assertEqual(response.redirect_chain[0], ('/redirects/further/', 302))
+        self.assertEqual(response.redirect_chain[1], ('/redirects/further/more/', 302))
+        self.assertEqual(response.redirect_chain[2], ('/no_template_view/', 302))
 
     def test_redirect_chain_to_non_existent(self):
         "You can follow a chain to a non-existent view"
@@ -507,21 +509,24 @@ class AssertRedirectsTests(TestCase):
     def test_redirect_scheme(self):
         "An assertion is raised if the response doesn't have the scheme specified in expected_url"
 
-        # Assure that original request scheme is preserved if no scheme specified in the redirect location
-        response = self.client.get('/redirect_view/', secure=True)
-        self.assertRedirects(response, 'https://testserver/get_view/')
-
         # For all possible True/False combinations of follow and secure
         for follow, secure in itertools.product([True, False], repeat=2):
             # always redirects to https
             response = self.client.get('/https_redirect_view/', follow=follow, secure=secure)
-            # no scheme to compare too, always succeeds
-            self.assertRedirects(response, '/secure_view/', status_code=302)
             # the goal scheme is https
             self.assertRedirects(response, 'https://testserver/secure_view/', status_code=302)
             with self.assertRaises(AssertionError):
                 self.assertRedirects(response, 'http://testserver/secure_view/', status_code=302)
 
+    @ignore_warnings(category=RemovedInDjango21Warning)
+    def test_full_path_in_expected_urls(self):
+        """
+        Test that specifying a full URL as assertRedirects expected_url still
+        work as backwards compatible behavior until Django 2.1.
+        """
+        response = self.client.get('/redirect_view/')
+        self.assertRedirects(response, 'http://testserver/get_view/')
+
 
 @override_settings(ROOT_URLCONF='test_client_regress.urls')
 class AssertFormErrorTests(TestCase):
@@ -852,7 +857,7 @@ class LoginTests(TestDataMixin, TestCase):
         # At this points, the self.client isn't logged in.
         # Check that assertRedirects uses the original client, not the
         # default client.
-        self.assertRedirects(response, "http://testserver/get_view/")
+        self.assertRedirects(response, "/get_view/")
 
 
 @override_settings(

+ 0 - 1
tests/view_tests/generic_urls.py

@@ -32,7 +32,6 @@ urlpatterns = [
     url(r'^accounts/logout/$', auth_views.logout),
 
     # Special URLs for particular regression cases.
-    url('^中文/$', views.redirect),
     url('^中文/target/$', views.index_page),
 ]
 

+ 4 - 4
tests/view_tests/tests/test_i18n.py

@@ -31,7 +31,7 @@ class I18NTests(TestCase):
         for lang_code, lang_name in settings.LANGUAGES:
             post_data = dict(language=lang_code, next='/')
             response = self.client.post('/i18n/setlang/', data=post_data)
-            self.assertRedirects(response, 'http://testserver/')
+            self.assertRedirects(response, '/')
             self.assertEqual(self.client.session[LANGUAGE_SESSION_KEY], lang_code)
 
     def test_setlang_unsafe_next(self):
@@ -42,7 +42,7 @@ class I18NTests(TestCase):
         lang_code, lang_name = settings.LANGUAGES[0]
         post_data = dict(language=lang_code, next='//unsafe/redirection/')
         response = self.client.post('/i18n/setlang/', data=post_data)
-        self.assertEqual(response.url, 'http://testserver/')
+        self.assertEqual(response.url, '/')
         self.assertEqual(self.client.session[LANGUAGE_SESSION_KEY], lang_code)
 
     def test_setlang_reversal(self):
@@ -76,13 +76,13 @@ class I18NTests(TestCase):
             follow=True, HTTP_REFERER='/en/translated/'
         )
         self.assertEqual(self.client.session[LANGUAGE_SESSION_KEY], 'nl')
-        self.assertRedirects(response, 'http://testserver/nl/vertaald/')
+        self.assertRedirects(response, '/nl/vertaald/')
         # And reverse
         response = self.client.post(
             '/i18n/setlang/', data={'language': 'en'},
             follow=True, HTTP_REFERER='/nl/vertaald/'
         )
-        self.assertRedirects(response, 'http://testserver/en/translated/')
+        self.assertRedirects(response, '/en/translated/')
 
     def test_jsi18n(self):
         """The javascript_catalog can be deployed with language settings"""

+ 0 - 11
tests/view_tests/tests/test_specials.py

@@ -11,17 +11,6 @@ class URLHandling(TestCase):
     """
     redirect_target = "/%E4%B8%AD%E6%96%87/target/"
 
-    def test_combining_redirect(self):
-        """
-        Tests that redirecting to an IRI, requiring encoding before we use it
-        in an HTTP response, is handled correctly. In this case the arg to
-        HttpRedirect is ASCII but the current request path contains non-ASCII
-        characters so this test ensures the creation of the full path with a
-        base non-ASCII part is handled correctly.
-        """
-        response = self.client.get('/中文/')
-        self.assertRedirects(response, self.redirect_target)
-
     def test_nonascii_redirect(self):
         """
         Tests that a non-ASCII argument to HttpRedirect is handled properly.

+ 1 - 10
tests/view_tests/views.py

@@ -6,9 +6,7 @@ import sys
 
 from django.core.exceptions import PermissionDenied, SuspiciousOperation
 from django.core.urlresolvers import get_resolver
-from django.http import (
-    Http404, HttpResponse, HttpResponseRedirect, JsonResponse,
-)
+from django.http import Http404, HttpResponse, JsonResponse
 from django.shortcuts import render, render_to_response
 from django.template import TemplateDoesNotExist
 from django.utils.log import getLogger
@@ -71,13 +69,6 @@ class Http404View(View):
         raise Http404("Testing class-based technical 404.")
 
 
-def redirect(request):
-    """
-    Forces an HTTP redirect.
-    """
-    return HttpResponseRedirect("target/")
-
-
 def view_exception(request, n):
     raise BrokenException(except_args[int(n)])