diff options
| author | Jay Cox <jaycox@linear3d.com> | 2015-04-27 22:23:42 -0700 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2015-07-31 12:04:06 -0400 |
| commit | 434d309ef6dbecbfd2b322d3a1da78aa5cb05fa8 (patch) | |
| tree | 70fa5bf07614e81ca9d32ae1d105ce3b21cf1cdc /django/middleware/common.py | |
| parent | 1e2362ca0f2805e91204a7b005e5aed293b40c9e (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.py | 83 |
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'] |
