diff --git a/swift/common/swob.py b/swift/common/swob.py index 7a14d1cf10..4074328c28 100644 --- a/swift/common/swob.py +++ b/swift/common/swob.py @@ -55,7 +55,7 @@ from six.moves import urllib from swift.common.header_key_dict import HeaderKeyDict from swift.common.utils import UTC, reiterate, split_path, Timestamp, pairs, \ - close_if_possible, closing_if_possible, config_true_value + close_if_possible, closing_if_possible, config_true_value, drain_and_close from swift.common.exceptions import InvalidTimestamp @@ -1400,6 +1400,7 @@ class Response(object): if self.request and self.request.method == 'HEAD': # We explicitly do NOT want to set self.content_length to 0 here + drain_and_close(app_iter) # be friendly to our app_iter return [b''] if self.conditional_response and self.request and \ diff --git a/test/unit/common/middleware/test_object_versioning.py b/test/unit/common/middleware/test_object_versioning.py index cd2d27bf04..1e5496e5f2 100644 --- a/test/unit/common/middleware/test_object_versioning.py +++ b/test/unit/common/middleware/test_object_versioning.py @@ -552,6 +552,20 @@ class ObjectVersioningTestCase(ObjectVersioningBaseTestCase): self.assertIn( ('X-Symlink-Target', 'c/o?version-id=0000001234.00000'), headers) + self.assertEqual(body, b'') + # N.B. HEAD req already works with existing registered GET response + req = Request.blank( + '/v1/a/c/o?symlink=get', method='HEAD', + environ={'swift.cache': self.cache_version_on}) + status, headers, body = self.call_ov(req) + self.assertEqual(status, '200 OK') + self.assertEqual(len(self.authorized), 1) + self.assertRequestEqual(req, self.authorized[0]) + self.assertIn(('X-Object-Version-Id', '0000001234.00000'), headers) + self.assertIn( + ('X-Symlink-Target', 'c/o?version-id=0000001234.00000'), + headers) + self.assertEqual(body, b'') def test_put_object_no_versioning(self): self.app.register( diff --git a/test/unit/common/test_swob.py b/test/unit/common/test_swob.py index 2a8ed09c73..87cd43fdea 100644 --- a/test/unit/common/test_swob.py +++ b/test/unit/common/test_swob.py @@ -28,6 +28,8 @@ from six.moves.urllib.parse import quote import swift.common.swob as swob from swift.common import utils, exceptions +from test.unit.common.middleware.helpers import LeakTrackingIter + class TestHeaderEnvironProxy(unittest.TestCase): def test_proxy(self): @@ -1232,16 +1234,34 @@ class TestResponse(unittest.TestCase): The Response object's __call__ method should be able to reify a Request object from the env it gets passed. """ + tracking = { + 'closed': 0, + 'read': 0, + } + + def mark_closed(*args): + tracking['closed'] += 1 + + def mark_read(*args): + tracking['read'] += 1 + def test_app(environ, start_response): start_response('200 OK', []) - return [b'hi'] + body = [b'hi'] + return LeakTrackingIter(body, mark_closed, mark_read, None) req = swob.Request.blank('/') req.method = 'HEAD' status, headers, app_iter = req.call_application(test_app) resp = swob.Response(status=status, headers=dict(headers), app_iter=app_iter) output_iter = resp(req.environ, lambda *_: None) - self.assertEqual(list(output_iter), [b'']) + with utils.closing_if_possible(output_iter): + body = b''.join(output_iter) + self.assertEqual(body, b'') + self.assertEqual(tracking, { + 'closed': 1, + 'read': 1, + }) def test_call_preserves_closeability(self): def test_app(environ, start_response):