diff --git a/doc/source/misc.rst b/doc/source/misc.rst index 407660e5a2..87fe534c0c 100644 --- a/doc/source/misc.rst +++ b/doc/source/misc.rst @@ -33,6 +33,16 @@ Utils :members: :show-inheritance: +.. _swob: + +Swob +==== + +.. automodule:: swift.common.swob + :members: + :show-inheritance: + :special-members: __call__ + .. _acls: ACLs diff --git a/swift/common/swob.py b/swift/common/swob.py index 8ee1ead450..9687eeb8de 100644 --- a/swift/common/swob.py +++ b/swift/common/swob.py @@ -1186,11 +1186,31 @@ class Response(object): return self.status_int // 100 == 2 def __call__(self, env, start_response): + """ + Respond to the WSGI request. + + .. warning:: + + This will translate any relative Location header value to an + absolute URL using the WSGI environment's HOST_URL as a + prefix, as RFC 2616 specifies. + + However, it is quite common to use relative redirects, + especially when it is difficult to know the exact HOST_URL + the browser would have used when behind several CNAMEs, CDN + services, etc. All modern browsers support relative + redirects. + + To skip over RFC enforcement of the Location header value, + you may set ``env['swift.leave_relative_location'] = True`` + in the WSGI environment. + """ if not self.request: self.request = Request(env) self.environ = env app_iter = self._response_iter(self.app_iter, self._body) - if 'location' in self.headers: + if 'location' in self.headers and \ + not env.get('swift.leave_relative_location'): self.location = self.absolute_location() start_response(self.status, self.headers.items()) return app_iter diff --git a/test/unit/common/test_swob.py b/test/unit/common/test_swob.py index c740b435a9..c0de63d045 100644 --- a/test/unit/common/test_swob.py +++ b/test/unit/common/test_swob.py @@ -1034,6 +1034,19 @@ class TestResponse(unittest.TestCase): ''.join(resp(req.environ, start_response)) self.assertEquals(resp.location, 'http://www.google.com/') + def test_location_no_rewrite_when_told_not_to(self): + def start_response(env, headers): + pass + req = swift.common.swob.Request.blank( + '/', environ={'SERVER_NAME': 'local', 'SERVER_PORT': 81, + 'swift.leave_relative_location': True}) + del req.environ['HTTP_HOST'] + resp = self._get_response() + resp.location = '/something' + # read response + ''.join(resp(req.environ, start_response)) + self.assertEquals(resp.location, '/something') + def test_app_iter(self): def start_response(env, headers): pass