Fixed #987 -- Convert relative URI portions into absolute URIs in HTTP Location headers. Based on a patch from SmileyChris.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@6164 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick 2007-09-14 05:28:00 +00:00
parent dc785104b8
commit e70d7e6064
5 changed files with 55 additions and 9 deletions

View File

@ -50,6 +50,10 @@ class BaseHandler(object):
def get_response(self, request): def get_response(self, request):
"Returns an HttpResponse object for the given HttpRequest" "Returns an HttpResponse object for the given HttpRequest"
response = self._real_get_response(request)
return fix_location_header(request, response)
def _real_get_response(self, request):
from django.core import exceptions, urlresolvers from django.core import exceptions, urlresolvers
from django.core.mail import mail_admins from django.core.mail import mail_admins
from django.conf import settings from django.conf import settings
@ -129,3 +133,16 @@ class BaseHandler(object):
"Helper function to return the traceback as a string" "Helper function to return the traceback as a string"
import traceback import traceback
return '\n'.join(traceback.format_exception(*(exc_info or sys.exc_info()))) return '\n'.join(traceback.format_exception(*(exc_info or sys.exc_info())))
def fix_location_header(request, response):
"""
Ensure that we always use an absolute URI in any location header in the
response. This is required by RFC 2616, section 14.30.
Code constructing response objects is free to insert relative paths and
this function converts them to absolute paths.
"""
if 'Location' in response.headers and http.get_host(request):
response['Location'] = request.build_absolute_uri(response['Location'])
return response

View File

@ -2,6 +2,7 @@ import os
from Cookie import SimpleCookie from Cookie import SimpleCookie
from pprint import pformat from pprint import pformat
from urllib import urlencode from urllib import urlencode
from urlparse import urljoin
from django.utils.datastructures import MultiValueDict, FileDict from django.utils.datastructures import MultiValueDict, FileDict
from django.utils.encoding import smart_str, iri_to_uri, force_unicode from django.utils.encoding import smart_str, iri_to_uri, force_unicode
@ -46,6 +47,20 @@ class HttpRequest(object):
def get_full_path(self): def get_full_path(self):
return '' return ''
def build_absolute_uri(self, location=None):
"""
Builds an absolute URI from the location and the variables available in
this request. If no location is specified, the absolute URI is built on
``request.get_full_path()``.
"""
if not location:
location = request.get_full_path()
if not ':' in location:
current_uri = '%s://%s%s' % (self.is_secure() and 'https' or 'http',
get_host(self), self.path)
location = urljoin(current_uri, location)
return location
def is_secure(self): def is_secure(self):
return os.environ.get("HTTPS") == "on" return os.environ.get("HTTPS") == "on"

View File

@ -84,12 +84,8 @@ class TestCase(unittest.TestCase):
self.assertEqual(response.status_code, status_code, self.assertEqual(response.status_code, status_code,
("Response didn't redirect as expected: Response code was %d" ("Response didn't redirect as expected: Response code was %d"
" (expected %d)" % (response.status_code, status_code))) " (expected %d)" % (response.status_code, status_code)))
scheme, netloc, path, query, fragment = urlsplit(response['Location']) url = response['Location']
url = path scheme, netloc, path, query, fragment = urlsplit(url)
if query:
url += '?' + query
if fragment:
url += '#' + fragment
self.assertEqual(url, expected_url, self.assertEqual(url, expected_url,
"Response redirected to '%s', expected '%s'" % (url, expected_url)) "Response redirected to '%s', expected '%s'" % (url, expected_url))

View File

@ -161,6 +161,16 @@ Methods
Example: ``"/music/bands/the_beatles/?print=true"`` Example: ``"/music/bands/the_beatles/?print=true"``
``build_absolute_uri(location)``
Returns the absolute URI form of ``location``. If no location is provided,
the location will be set to ``request.get_full_path()``.
If the location is already an absolute URI, it will not be altered.
Otherwise the absolute URI is built using the server variables available in
this request.
Example: ``"http://example.com/music/bands/the_beatles/?print=true"``
``is_secure()`` ``is_secure()``
Returns ``True`` if the request is secure; that is, if it was made with Returns ``True`` if the request is secure; that is, if it was made with
HTTPS. HTTPS.

View File

@ -83,10 +83,14 @@ class ClientTest(TestCase):
def test_redirect(self): def test_redirect(self):
"GET a URL that redirects elsewhere" "GET a URL that redirects elsewhere"
response = self.client.get('/test_client/redirect_view/') response = self.client.get('/test_client/redirect_view/')
# Check that the response was a 302 (redirect) # Check that the response was a 302 (redirect)
self.assertRedirects(response, '/test_client/get_view/') self.assertRedirects(response, '/test_client/get_view/')
client_providing_host = Client(HTTP_HOST='django.testserver')
response = client_providing_host.get('/test_client/redirect_view/')
# Check that the response was a 302 (redirect) with absolute URI
self.assertRedirects(response, 'http://django.testserver/test_client/get_view/')
def test_redirect_with_query(self): def test_redirect_with_query(self):
"GET a URL that redirects with given GET parameters" "GET a URL that redirects with given GET parameters"
response = self.client.get('/test_client/redirect_view/', {'var': 'value'}) response = self.client.get('/test_client/redirect_view/', {'var': 'value'})
@ -97,10 +101,14 @@ class ClientTest(TestCase):
def test_permanent_redirect(self): def test_permanent_redirect(self):
"GET a URL that redirects permanently elsewhere" "GET a URL that redirects permanently elsewhere"
response = self.client.get('/test_client/permanent_redirect_view/') response = self.client.get('/test_client/permanent_redirect_view/')
# Check that the response was a 301 (permanent redirect) # Check that the response was a 301 (permanent redirect)
self.assertRedirects(response, '/test_client/get_view/', status_code=301) self.assertRedirects(response, '/test_client/get_view/', status_code=301)
client_providing_host = Client(HTTP_HOST='django.testserver')
response = client_providing_host.get('/test_client/permanent_redirect_view/')
# Check that the response was a 301 (permanent redirect) with absolute URI
self.assertRedirects(response, 'http://django.testserver/test_client/get_view/', status_code=301)
def test_redirect_to_strange_location(self): def test_redirect_to_strange_location(self):
"GET a URL that redirects to a non-200 page" "GET a URL that redirects to a non-200 page"
response = self.client.get('/test_client/double_redirect_view/') response = self.client.get('/test_client/double_redirect_view/')