fix swob HEAD handling for leak tracking

We added a "safety net" in swob that won't let HEAD responses return a
body.  It seems like we don't currently have many unittests that both
enforce leak tracking and ALSO make HEAD requests.  The way swob works
currently it's not possible for even a "well behaved client" to get a
clean assert with FakeSwift's leak tracking because the leak tracking
iterator isn't returned to the client to close and swob doesn't do
anything to close down and consume the HEAD response iterator it's
throwing away.

RelatedChange: I168e147aae7c1728e7e3fdabb7fba6f2d747d937
Change-Id: I079315ebeb772eaad1bf190fefd132b4087b4897
This commit is contained in:
Clay Gerrard
2023-08-15 12:55:21 -05:00
parent cc59929412
commit 9e065e2d23
3 changed files with 38 additions and 3 deletions

View File

@@ -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 \

View File

@@ -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(

View File

@@ -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):