Django

Code

Ticket #3228: smartslash-3.diff

File smartslash-3.diff, 10.2 kB (added by Andy Gayton <andy-django@thecablelounge.com>, 9 months ago)
  • django/middleware/common.py

    old new  
    55from django import http 
    66from django.core.mail import mail_managers 
    77from django.utils.http import urlquote 
     8from django.core import urlresolvers 
    89 
    910class CommonMiddleware(object): 
    1011    """ 
     
    1617          this middleware appends missing slashes and/or prepends missing 
    1718          "www."s. 
    1819 
     20            - If APPEND_SLASH is set and the initial URL doesn't end with a 
     21              slash, and it is not found in urlpatterns, a new URL is formed by 
     22              appending a slash at the end. If this new URL is found in 
     23              urlpatterns, then an HTTP-redirect is returned to this new URL; 
     24              otherwise the initial URL is processed as usual.  
     25 
    1926        - ETags: If the USE_ETAGS setting is set, ETags will be calculated from 
    2027          the entire page content and Not Modified responses will be returned 
    2128          appropriately. 
     
    3340                if user_agent_regex.search(request.META['HTTP_USER_AGENT']): 
    3441                    return http.HttpResponseForbidden('<h1>Forbidden</h1>') 
    3542 
    36         # Check for a redirect based on settings.APPEND_SLASH and settings.PREPEND_WWW 
     43        # Check for a redirect based on settings.APPEND_SLASH 
     44        # and settings.PREPEND_WWW 
    3745        host = request.get_host() 
    3846        old_url = [host, request.path] 
    3947        new_url = old_url[:] 
    40         if settings.PREPEND_WWW and old_url[0] and not old_url[0].startswith('www.'): 
     48 
     49        if (settings.PREPEND_WWW and old_url[0] and 
     50                not old_url[0].startswith('www.')): 
    4151            new_url[0] = 'www.' + old_url[0] 
    42         # Append a slash if append_slash is set and the URL doesn't have a 
    43         # trailing slash or a file extension. 
    44         if settings.APPEND_SLASH and (not old_url[1].endswith('/')) and ('.' not in old_url[1].split('/')[-1]): 
    45             new_url[1] = new_url[1] + '/' 
    46             if settings.DEBUG and request.method == 'POST': 
    47                 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]) 
     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 
    4869        if new_url != old_url: 
    49             # Redirect 
    50             if new_url[0]: 
    51                 newurl = "%s://%s%s" % (request.is_secure() and 'https' or 'http', new_url[0], urlquote(new_url[1])) 
     70            # Redirect if the target url exists 
     71            try: 
     72                urlresolvers.resolve(new_url[1]) 
     73            except urlresolvers.Resolver404: 
     74                pass 
    5275            else: 
    53                 newurl = urlquote(new_url[1]) 
    54             if request.GET: 
    55                 newurl += '?' + request.GET.urlencode() 
    56             return http.HttpResponsePermanentRedirect(newurl) 
     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) 
    5785 
    5886        return None 
    5987 
  • tests/regressiontests/middleware/tests.py

    old new  
     1# -*- coding: utf-8 -*- 
     2 
     3import unittest 
     4 
     5from django.test import TestCase  
     6from django.http import HttpRequest 
     7from django.middleware.common import CommonMiddleware 
     8from django.conf import settings 
     9 
     10class CommonMiddlewareTest(TestCase): 
     11    def _get_request(self, path): 
     12        request = HttpRequest() 
     13        request.META = { 
     14            'SERVER_NAME': 'testserver', 
     15            'SERVER_PORT': 80, 
     16        } 
     17        request.path = "/middleware/%s" % path 
     18        return request 
     19 
     20    def test_append_slash_have_slash(self): 
     21        """ 
     22        tests that urls with slashes go unmolested 
     23        """ 
     24        settings.APPEND_SLASH = True 
     25        request = self._get_request('slash/') 
     26        self.assertEquals(CommonMiddleware().process_request(request), None) 
     27 
     28    def test_append_slash_slashless_resource(self): 
     29        """ 
     30        tests that matches to explicit slashless urls go unmolested 
     31        """ 
     32        settings.APPEND_SLASH = True 
     33        request = self._get_request('noslash') 
     34        self.assertEquals(CommonMiddleware().process_request(request), None) 
     35 
     36    def test_append_slash_slashless_unknown(self): 
     37        """ 
     38        tests that APPEND_SLASH doesn't redirect to unknown resources 
     39        """ 
     40        settings.APPEND_SLASH = True 
     41        request = self._get_request('unknown') 
     42        self.assertEquals(CommonMiddleware().process_request(request), None) 
     43 
     44    def test_append_slash_redirect(self): 
     45        """ 
     46        tests that APPEND_SLASH redirects slashless urls to a valid pattern 
     47        """ 
     48        settings.APPEND_SLASH = True 
     49        request = self._get_request('slash') 
     50        r = CommonMiddleware().process_request(request) 
     51        self.assertEquals(r.status_code, 301) 
     52        self.assertEquals(r['Location'], 'http://testserver/middleware/slash/') 
     53 
     54    def test_append_slash_no_redirect_on_POST_in_DEBUG(self): 
     55        """ 
     56        tests that while in debug mode, an exception is raised with a warning 
     57        when a failed attempt is made to POST to an url which would normally be 
     58        redirected to a slashed version 
     59        """ 
     60        settings.APPEND_SLASH = True 
     61        settings.DEBUG = True 
     62        request = self._get_request('slash') 
     63        request.method = 'POST' 
     64        self.assertRaises( 
     65            RuntimeError, 
     66            CommonMiddleware().process_request, 
     67            request) 
     68        try: 
     69            CommonMiddleware().process_request(request) 
     70        except RuntimeError, e: 
     71            self.assertTrue('end in a slash' in str(e)) 
     72 
     73    def test_append_slash_disabled(self): 
     74        """ 
     75        tests disabling append slash functionality 
     76        """ 
     77        settings.APPEND_SLASH = False 
     78        request = self._get_request('slash') 
     79        self.assertEquals(CommonMiddleware().process_request(request), None) 
     80 
     81    def test_append_slash_quoted(self): 
     82        """ 
     83        tests that urls which require quoting are redirected to their slash 
     84        version ok 
     85        """ 
     86        settings.APPEND_SLASH = True 
     87        request = self._get_request('needsquoting#') 
     88        r = CommonMiddleware().process_request(request) 
     89        self.assertEquals(r.status_code, 301) 
     90        self.assertEquals( 
     91            r['Location'], 
     92            'http://testserver/middleware/needsquoting%23/') 
     93 
  • tests/regressiontests/middleware/urls.py

    old new  
     1from django.conf.urls.defaults import patterns 
     2 
     3urlpatterns = patterns('', 
     4    (r'^noslash$', 'view'), 
     5    (r'^slash/$', 'view'), 
     6    (r'^needsquoting#/$', 'view'), 
     7) 
  • tests/urls.py

    old new  
    1414     
    1515    # django built-in views 
    1616    (r'^views/', include('regressiontests.views.urls')), 
     17 
     18    # test urlconf for middleware tests 
     19    (r'^middleware/', include('regressiontests.middleware.urls')), 
    1720) 
  • docs/middleware.txt

    old new  
    5858  which should be a list of strings. 
    5959 
    6060* Performs URL rewriting based on the ``APPEND_SLASH`` and ``PREPEND_WWW`` 
    61   settings. If ``APPEND_SLASH`` is ``True``, URLs that lack a trailing 
    62   slash will be redirected to the same URL with a trailing slash, unless the 
    63   last component in the path contains a period. So ``foo.com/bar`` is 
    64   redirected to ``foo.com/bar/``, but ``foo.com/bar/file.txt`` is passed 
    65   through unchanged. 
     61  settings. 
    6662 
     63  **New in Django development version:** If ``APPEND_SLASH`` is ``True`` and 
     64  the initial URL doesn't end with a slash, and it is not found in urlpatterns, 
     65  a new URL is formed by appending a slash at the end. If this new URL is found 
     66  in urlpatterns, then an HTTP-redirect is returned to this new URL; otherwise 
     67  the initial URL is processed as usual.  
     68 
     69  So ``foo.com/bar`` is will be redirected to ``foo.com/bar/`` if you do not 
     70  have a valid urlpattern for ``foo.com/bar``, and do have a valid urlpattern 
     71  for ``foo.com/bar/``. 
     72   
    6773  If ``PREPEND_WWW`` is ``True``, URLs that lack a leading "www." will be 
    6874  redirected to the same URL with a leading "www." 
    6975