From e2929ec58aadcfd2c08b53790156344d93392b94 Mon Sep 17 00:00:00 2001 From: Darrell Bishop Date: Thu, 10 Jan 2013 13:08:22 -0800 Subject: [PATCH] Fix HEAD request response when request not given to response. If a middleware (swift3, I'm looking at you), doesn't pass a Request object into the Response constructor, Response._response_iter cannot know to send zero bytes in the body of the HEAD response. This patch fixes this usage of swob by making Response.__call__ helpfully reify self.request from env if it wasn't already set by the Response object's constructor. This fixes a bug in swift3 + swob-enabled-Swift where HEAD requests to swift3 resulted in a response with a body in violation of the relevant RFC and confusing clients. Thanks to kostecky for finding the bug and describing it accurately. Change-Id: I2bdb098052b161e1cddf1e4e482ab4dfafeb18c0 --- swift/common/swob.py | 2 ++ test/unit/common/test_swob.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/swift/common/swob.py b/swift/common/swob.py index 47a9069cee..6968aaefb3 100755 --- a/swift/common/swob.py +++ b/swift/common/swob.py @@ -997,6 +997,8 @@ class Response(object): return self.host_url() + self.location def __call__(self, env, start_response): + 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: diff --git a/test/unit/common/test_swob.py b/test/unit/common/test_swob.py index 3c823b7f5f..c42a1fb98b 100755 --- a/test/unit/common/test_swob.py +++ b/test/unit/common/test_swob.py @@ -462,6 +462,24 @@ class TestResponse(unittest.TestCase): resp.body = u'\N{SNOWMAN}' self.assertEquals(resp.body, u'\N{SNOWMAN}'.encode('utf-8')) + def test_call_reifies_request_if_necessary(self): + """ + The actual bug was a HEAD response coming out with a body because the + Request object wasn't passed into the Response object's constructor. + The Response object's __call__ method should be able to reify a + Request object from the env it gets passed. + """ + def test_app(environ, start_response): + start_response('200 OK', []) + return ['hi'] + req = swift.common.swob.Request.blank('/') + req.method = 'HEAD' + status, headers, app_iter = req.call_application(test_app) + resp = swift.common.swob.Response(status=status, headers=dict(headers), + app_iter=app_iter) + output_iter = resp(req.environ, lambda *_: None) + self.assertEquals(list(output_iter), ['']) + def test_location_rewrite(self): def start_response(env, headers): pass