Ticket #6213: common_middleware.2.py.diff

File common_middleware.2.py.diff, 8.9 KB (added by esaj, 7 years ago)

Updated patch with unit tests.

  • django/middleware/common.py

     
    4040                if user_agent_regex.search(request.META['HTTP_USER_AGENT']):
    4141                    return http.HttpResponseForbidden('<h1>Forbidden</h1>')
    4242
    43         # Check for a redirect based on settings.APPEND_SLASH
    44         # and settings.PREPEND_WWW
    45         host = request.get_host()
    46         old_url = [host, request.path]
    47         new_url = old_url[:]
     43        _response = _handle_redirects(request)
     44        if _response:
     45            return _response
    4846
    49         if (settings.PREPEND_WWW and old_url[0] and
    50                 not old_url[0].startswith('www.')):
    51             new_url[0] = 'www.' + old_url[0]
    52 
    53         # Append a slash if APPEND_SLASH is set and the URL doesn't have a
    54         # trailing slash and there is no pattern for the current path
    55         if settings.APPEND_SLASH and (not old_url[1].endswith('/')):
    56             try:
    57                 urlresolvers.resolve(request.path)
    58             except urlresolvers.Resolver404:
    59                 new_url[1] = new_url[1] + '/'
    60                 if settings.DEBUG and request.method == 'POST':
    61                     raise RuntimeError, (""
    62                     "You called this URL via POST, but the URL doesn't end "
    63                     "in a slash and you have APPEND_SLASH set. Django can't "
    64                     "redirect to the slash URL while maintaining POST data. "
    65                     "Change your form to point to %s%s (note the trailing "
    66                     "slash), or set APPEND_SLASH=False in your Django "
    67                     "settings.") % (new_url[0], new_url[1])
    68 
    69         if new_url != old_url:
    70             # Redirect if the target url exists
    71             try:
    72                 urlresolvers.resolve(new_url[1])
    73             except urlresolvers.Resolver404:
    74                 pass
    75             else:
    76                 if new_url[0]:
    77                     newurl = "%s://%s%s" % (
    78                         request.is_secure() and 'https' or 'http',
    79                         new_url[0], urlquote(new_url[1]))
    80                 else:
    81                     newurl = urlquote(new_url[1])
    82                 if request.GET:
    83                     newurl += '?' + request.GET.urlencode()
    84                 return http.HttpResponsePermanentRedirect(newurl)
    85 
    8647        return None
    8748
    8849    def process_response(self, request, response):
    8950        "Check for a flat page (for 404s) and calculate the Etag, if needed."
    9051        if response.status_code == 404:
     52            _response = _handle_redirects(request, resolve=False)
     53            if _response:
     54                return _response
    9155            if settings.SEND_BROKEN_LINK_EMAILS:
    9256                # If the referrer was from an internal link or a non-search-engine site,
    9357                # send a note to the managers.
     
    11882
    11983        return response
    12084
     85def _handle_redirects(request, resolve=True):
     86    host = request.get_host()
     87    old_url = [host, request.path]
     88    new_url = old_url[:]
     89
     90    if (settings.PREPEND_WWW and old_url[0] and
     91            not old_url[0].startswith('www.')):
     92        new_url[0] = 'www.' + old_url[0]
     93
     94    # Append a slash if APPEND_SLASH is set and the URL doesn't have a
     95    # trailing slash and there is no pattern for the current path
     96    if settings.APPEND_SLASH and (not old_url[1].endswith('/')):
     97        if resolve:
     98            try:
     99                urlresolvers.resolve(request.path)
     100            except urlresolvers.Resolver404:
     101                new_url[1] = new_url[1] + '/'
     102        else:
     103            new_url[1] = new_url[1] + '/'
     104
     105    if new_url != old_url:
     106        # Redirect if the target url exists
     107        try:
     108            if resolve and new_url[0] == new_url[0] and new_url[1] != old_url[1]:
     109                urlresolvers.resolve(new_url[1])
     110        except urlresolvers.Resolver404:
     111            pass
     112        else:
     113            if new_url[0]:
     114                newurl = "%s://%s%s" % (
     115                    request.is_secure() and 'https' or 'http',
     116                    new_url[0], urlquote(new_url[1]))
     117            else:
     118                newurl = urlquote(new_url[1])
     119            if request.GET:
     120                newurl += '?' + request.GET.urlencode()
     121            if settings.DEBUG and request.method == 'POST':
     122                raise RuntimeError, (""
     123                "You called this URL via POST, but the URL doesn't end "
     124                "in a slash and you have APPEND_SLASH set. Django can't "
     125                "redirect to the slash URL while maintaining POST data. "
     126                "Change your form to point to %s%s (note the trailing "
     127                "slash), or set APPEND_SLASH=False in your Django "
     128                "settings.") % (new_url[0], new_url[1])
     129            return http.HttpResponsePermanentRedirect(newurl)
     130
    121131def _is_ignorable_404(uri):
    122132    "Returns True if a 404 at the given URL *shouldn't* notify the site managers"
    123133    for start in settings.IGNORABLE_404_STARTS:
  • tests/regressiontests/middleware/tests.py

     
    11# -*- coding: utf-8 -*-
    22
    33from django.test import TestCase
    4 from django.http import HttpRequest
     4from django.http import HttpRequest, HttpResponse, HttpResponseNotFound
    55from django.middleware.common import CommonMiddleware
    66from django.conf import settings
    77
     
    8989        self.assertEquals(
    9090            r['Location'],
    9191            'http://testserver/middleware/needsquoting%23/')
     92
     93    def test_append_slash_enabled_custom_middleware_slash(self):
     94        """
     95        Tests that if some intermediate middleware (e.g.
     96        FlatpageFallbackMiddleware) has processed the request and returned a
     97        response, this is not redirected.
     98        """
     99        settings.APPEND_SLASH = True
     100        request = self._get_request('custom/slash')
     101        common_middleware = CommonMiddleware()
     102        custom_middleware = CustomMiddleware()
     103        response = common_middleware.process_request(request)
     104        if response is None:
     105            response = custom_middleware.process_request(request)
     106            response = custom_middleware.process_response(request, response)
     107        response = common_middleware.process_response(request, response)
     108        self.assertEquals(response.status_code, 301)
     109        self.assertEquals(
     110            response['Location'],
     111            'http://testserver/middleware/custom/slash/')
     112
     113    def test_append_slash_disabled_custom_middleware_slash(self):
     114        """
     115        Tests that if some intermediate middleware (e.g.
     116        FlatpageFallbackMiddleware) has processed the request and returned a
     117        response, this is not redirected.
     118        """
     119        settings.APPEND_SLASH = False
     120        request = self._get_request('custom/slash')
     121        common_middleware = CommonMiddleware()
     122        custom_middleware = CustomMiddleware()
     123        response = common_middleware.process_request(request)
     124        self.assertEquals(response, None)
     125        response = custom_middleware.process_request(request)
     126        self.assertEquals(response.status_code, 404)
     127        response = custom_middleware.process_response(request, response)
     128        self.assertEquals(response.status_code, 404)
     129        response = common_middleware.process_response(request, response)
     130        self.assertEquals(response.status_code, 404)
     131
     132    def test_append_slash_custom_middleware_slashless(self):
     133        """
     134        Tests that if some intermediate middleware (e.g.
     135        FlatpageFallbackMiddleware) has processed the request and returned a
     136        response, this is not redirected.
     137        """
     138        for append_slash in (True, False):
     139            settings.APPEND_SLASH = append_slash
     140            request = self._get_request('custom/noslash')
     141            common_middleware = CommonMiddleware()
     142            custom_middleware = CustomMiddleware()
     143            response = common_middleware.process_request(request)
     144            self.assertEquals(response, None)
     145            response = custom_middleware.process_request(request)
     146            self.assertContains(response, 'Custom middleware response')
     147            response = custom_middleware.process_response(request, response)
     148            self.assertContains(response, 'Custom middleware response')
     149            response = common_middleware.process_response(request, response)
     150            self.assertContains(response, 'Custom middleware response')
     151
     152class CustomMiddleware(object):
     153    def process_request(self, request):
     154        if request.path in ('/middleware/custom/slash/', '/middleware/custom/noslash'):
     155            return HttpResponse('Custom middleware response')
     156        return HttpResponseNotFound()
     157    def process_response(self, request, response):
     158        return response
Back to Top