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:
parent
dc785104b8
commit
e70d7e6064
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -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))
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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/')
|
||||||
|
|
Loading…
Reference in New Issue