Ticket #3228: smartslash-2.diff

File smartslash-2.diff, 7.6 KB (added by andy-django@…, 8 years ago)
  • django/middleware/common.py

     
    11from django.conf import settings
    22from django import http
    33from django.core.mail import mail_managers
     4from django.core import urlresolvers
    45import md5
    56import re
    67
     
    1415          this middleware appends missing slashes and/or prepends missing
    1516          "www."s.
    1617
     18            - If APPEND_SLASH is set and the initial URL doesn't end with a
     19              slash, and it is not found in urlpatterns, a new URL is formed by
     20              appending a slash at the end. If this new URL is found in
     21              urlpatterns, then an HTTP-redirect is returned to this new URL;
     22              otherwise the initial URL is processed as usual.
     23
    1724        - ETags: If the USE_ETAGS setting is set, ETags will be calculated from
    1825          the entire page content and Not Modified responses will be returned
    1926          appropriately.
     
    3138                if user_agent_regex.search(request.META['HTTP_USER_AGENT']):
    3239                    return http.HttpResponseForbidden('<h1>Forbidden</h1>')
    3340
    34         # Check for a redirect based on settings.APPEND_SLASH and settings.PREPEND_WWW
     41        # Check for a redirect based on settings.APPEND_SLASH
     42        # and settings.PREPEND_WWW
    3543        host = http.get_host(request)
    3644        old_url = [host, request.path]
    3745        new_url = old_url[:]
    38         if settings.PREPEND_WWW and old_url[0] and not old_url[0].startswith('www.'):
     46
     47        if (settings.PREPEND_WWW and old_url[0] and
     48                not old_url[0].startswith('www.')):
    3949            new_url[0] = 'www.' + old_url[0]
    40         # Append a slash if append_slash is set and the URL doesn't have a
    41         # trailing slash or a file extension.
    42         if settings.APPEND_SLASH and (not old_url[1].endswith('/')) and ('.' not in old_url[1].split('/')[-1]):
    43             new_url[1] = new_url[1] + '/'
    44             if settings.DEBUG and request.method == 'POST':
    45                 raise RuntimeError, "You called this URL via POST, but the URL doesn't end in a slash and you have APPEND_SLASH set. Django can't redirect to the slash URL while maintaining POST data. Change your form to point to %s%s (note the trailing slash), or set APPEND_SLASH=False in your Django settings." % (new_url[0], new_url[1])
     50
     51        # Append a slash if APPEND_SLASH is set and the URL doesn't have a
     52        # trailing slash and there is no pattern for the current path
     53        if settings.APPEND_SLASH and (not old_url[1].endswith('/')):
     54            try:
     55                urlresolvers.resolve(request.path)
     56            except urlresolvers.Resolver404:
     57                new_url[1] = new_url[1] + '/'
     58                if settings.DEBUG and request.method == 'POST':
     59                    raise RuntimeError, ""
     60                    "You called this URL via POST, but the URL doesn't end"
     61                    "in a slash and you have APPEND_SLASH set. Django can't"
     62                    "redirect to the slash URL while maintaining POST data."
     63                    "Change your form to point to %s%s (note the trailing"
     64                    "slash), or set APPEND_SLASH=False in your Django"
     65                    "settings." % (new_url[0], new_url[1])
     66
    4667        if new_url != old_url:
    47             # Redirect
    48             if new_url[0]:
    49                 newurl = "%s://%s%s" % (request.is_secure() and 'https' or 'http', new_url[0], new_url[1])
     68            # Redirect if the target url exists
     69            try:
     70                urlresolvers.resolve(new_url[1])
     71            except urlresolvers.Resolver404:
     72                pass
    5073            else:
    51                 newurl = new_url[1]
    52             if request.GET:
    53                 newurl += '?' + request.GET.urlencode()
    54             return http.HttpResponsePermanentRedirect(newurl)
     74                if new_url[0]:
     75                    newurl = "%s://%s%s" % (
     76                        request.is_secure() and 'https' or 'http',
     77                        new_url[0], new_url[1])
     78                else:
     79                    newurl = new_url[1]
     80                if request.GET:
     81                    newurl += '?' + request.GET.urlencode()
     82                return http.HttpResponsePermanentRedirect(newurl)
    5583
    5684        return None
    5785
  • tests/regressiontests/middleware/tests.py

     
     1# -*- coding: utf-8 -*-
     2
     3import unittest
     4
     5from django.http import HttpRequest
     6from django.middleware.common import CommonMiddleware
     7from django.conf import settings
     8
     9class CommonMiddlewareTest(unittest.TestCase):
     10    def setUp(self):
     11        # blow cached urls from previous test cases
     12        # django settings and urls are factored pretty badly for unit testing
     13        from django.core import urlresolvers
     14        keys = urlresolvers._resolver_cache.keys()
     15        for key in keys:
     16            del urlresolvers._resolver_cache[key]
     17        settings.ROOT_URLCONF = 'regressiontests.middleware.urls'
     18       
     19    def test_append_slash_have_slash(self):
     20        # test that urls with slashes go unmolested
     21        settings.APPEND_SLASH = True
     22        request = HttpRequest()
     23        request.path = '/slash/'
     24        self.assertEquals(CommonMiddleware().process_request(request), None)
     25
     26    def test_append_slash_slashless_resource(self):
     27        # test that matches to explicit slashless urls go unmolested
     28        settings.APPEND_SLASH = True
     29        request = HttpRequest()
     30        request.path = '/noslash'
     31        self.assertEquals(CommonMiddleware().process_request(request), None)
     32
     33    def test_append_slash_slashless_unknown(self):
     34        # test that APPEND_SLASH doesn't redirect to unknown resources
     35        settings.APPEND_SLASH = True
     36        request = HttpRequest()
     37        request.path = '/unknown'
     38        self.assertEquals(CommonMiddleware().process_request(request), None)
     39
     40    def test_append_slash_redirect(self):
     41        # test that APPEND_SLASH redirects slashless urls to a valid pattern
     42        settings.APPEND_SLASH = True
     43        request = HttpRequest()
     44        request.path = '/slash'
     45        r = CommonMiddleware().process_request(request)
     46        self.assertEquals(r.status_code, 301)
     47        self.assertEquals(r.headers['Location'], '/slash/')
     48
     49    def test_append_slash_no_redirect_on_POST_in_DEBUG(self):
     50        settings.APPEND_SLASH = True
     51        settings.DEBUG = True
     52        request = HttpRequest()
     53        request.path = '/slash'
     54        request.method = 'POST'
     55        self.assertRaises(RuntimeError, CommonMiddleware().process_request,
     56            request)
     57
     58    def test_append_slash_disabled(self):
     59        settings.APPEND_SLASH = False
     60        request = HttpRequest()
     61        request.path = '/slash'
     62        self.assertEquals(CommonMiddleware().process_request(request), None)
  • tests/regressiontests/middleware/urls.py

     
     1from django.conf.urls.defaults import patterns
     2
     3urlpatterns = patterns('',
     4    (r'^noslash$', 'view'),
     5    (r'^slash/$', 'view'),
     6)
Back to Top