summaryrefslogtreecommitdiff
path: root/django/middleware/common.py
diff options
context:
space:
mode:
authorJay Cox <jaycox@linear3d.com>2015-04-27 22:23:42 -0700
committerTim Graham <timograham@gmail.com>2015-07-31 12:04:06 -0400
commit434d309ef6dbecbfd2b322d3a1da78aa5cb05fa8 (patch)
tree70fa5bf07614e81ca9d32ae1d105ce3b21cf1cdc /django/middleware/common.py
parent1e2362ca0f2805e91204a7b005e5aed293b40c9e (diff)
Fixed #24720 -- Avoided resolving URLs that don't end in a slash twice in CommonMiddleware.
This speeds up affected requests by about 5%.
Diffstat (limited to 'django/middleware/common.py')
-rw-r--r--django/middleware/common.py83
1 files changed, 52 insertions, 31 deletions
diff --git a/django/middleware/common.py b/django/middleware/common.py
index 3d7f365af4..6773eff614 100644
--- a/django/middleware/common.py
+++ b/django/middleware/common.py
@@ -50,47 +50,68 @@ class CommonMiddleware(object):
if user_agent_regex.search(request.META['HTTP_USER_AGENT']):
raise PermissionDenied('Forbidden user agent')
- # Check for a redirect based on settings.APPEND_SLASH
- # and settings.PREPEND_WWW
+ # Check for a redirect based on settings.PREPEND_WWW
host = request.get_host()
- old_url = [host, request.get_full_path()]
- new_url = old_url[:]
- if (settings.PREPEND_WWW and old_url[0] and
- not old_url[0].startswith('www.')):
- new_url[0] = 'www.' + old_url[0]
+ if settings.PREPEND_WWW and host and not host.startswith('www.'):
+ host = 'www.' + host
- # Append a slash if APPEND_SLASH is set and the URL doesn't have a
- # trailing slash and there is no pattern for the current path
- if settings.APPEND_SLASH and (not old_url[1].endswith('/')):
+ # Check if we also need to append a slash so we can do it all
+ # with a single redirect.
+ if self.should_redirect_with_slash(request):
+ path = self.get_full_path_with_slash(request)
+ else:
+ path = request.get_full_path()
+
+ return self.response_redirect_class('%s://%s%s' % (request.scheme, host, path))
+
+ def should_redirect_with_slash(self, request):
+ """
+ Return True if settings.APPEND_SLASH is True and appending a slash to
+ the request path turns an invalid path into a valid one.
+ """
+ if settings.APPEND_SLASH and not request.get_full_path().endswith('/'):
urlconf = getattr(request, 'urlconf', None)
- if (not urlresolvers.is_valid_path(request.path_info, urlconf) and
- urlresolvers.is_valid_path("%s/" % request.path_info, urlconf)):
- new_url[1] = request.get_full_path(force_append_slash=True)
- if settings.DEBUG and request.method in ('POST', 'PUT', 'PATCH'):
- raise RuntimeError((""
- "You called this URL via %(method)s, 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 %(method)s data. "
- "Change your form to point to %(url)s (note the trailing "
- "slash), or set APPEND_SLASH=False in your Django "
- "settings.") % {'method': request.method, 'url': ''.join(new_url)})
+ return (
+ not urlresolvers.is_valid_path(request.path_info, urlconf)
+ and urlresolvers.is_valid_path('%s/' % request.path_info, urlconf)
+ )
+ return False
- if new_url == old_url:
- # No redirects required.
- return
- if new_url[0] != old_url[0]:
- newurl = "%s://%s%s" % (
- request.scheme,
- new_url[0], new_url[1])
- else:
- newurl = new_url[1]
- return self.response_redirect_class(newurl)
+ def get_full_path_with_slash(self, request):
+ """
+ Return the full path of the request with a trailing slash appended.
+
+ Raise a RuntimeError if settings.DEBUG is True and request.method is
+ GET, PUT, or PATCH.
+ """
+ new_path = request.get_full_path(force_append_slash=True)
+ if settings.DEBUG and request.method in ('POST', 'PUT', 'PATCH'):
+ raise RuntimeError(
+ "You called this URL via %(method)s, 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 %(method)s data. "
+ "Change your form to point to %(url)s (note the trailing "
+ "slash), or set APPEND_SLASH=False in your Django settings." % {
+ 'method': request.method,
+ 'url': request.get_host() + new_path,
+ }
+ )
+ return new_path
def process_response(self, request, response):
"""
Calculate the ETag, if needed.
+
+ When the status code of the response is 404, it may redirect to a path
+ with an appended slash if should_redirect_with_slash() returns True.
"""
+ # If the given URL is "Not Found", then check if we should redirect to
+ # a path with a slash appended.
+ if response.status_code == 404:
+ if self.should_redirect_with_slash(request):
+ return self.response_redirect_class(self.get_full_path_with_slash(request))
+
if settings.USE_ETAGS:
if response.has_header('ETag'):
etag = response['ETag']