Merge "Allow clients to send quoted ETags for static links"

This commit is contained in:
Zuul 2020-04-10 00:18:59 +00:00 committed by Gerrit Code Review
commit 2b7e80217d
3 changed files with 33 additions and 7 deletions

View File

@ -211,7 +211,7 @@ from swift.common.request_helpers import get_sys_meta_prefix, \
update_ignore_range_header update_ignore_range_header
from swift.common.swob import Request, HTTPBadRequest, HTTPTemporaryRedirect, \ from swift.common.swob import Request, HTTPBadRequest, HTTPTemporaryRedirect, \
HTTPException, HTTPConflict, HTTPPreconditionFailed, wsgi_quote, \ HTTPException, HTTPConflict, HTTPPreconditionFailed, wsgi_quote, \
wsgi_unquote, status_map wsgi_unquote, status_map, normalize_etag
from swift.common.http import is_success, HTTP_NOT_FOUND from swift.common.http import is_success, HTTP_NOT_FOUND
from swift.common.exceptions import LinkIterError from swift.common.exceptions import LinkIterError
from swift.common.header_key_dict import HeaderKeyDict from swift.common.header_key_dict import HeaderKeyDict
@ -285,7 +285,7 @@ def _validate_and_prep_request_headers(req):
raise HTTPBadRequest( raise HTTPBadRequest(
body='Symlink cannot target itself', body='Symlink cannot target itself',
request=req, content_type='text/plain') request=req, content_type='text/plain')
etag = req.headers.get(TGT_ETAG_SYMLINK_HDR, None) etag = normalize_etag(req.headers.get(TGT_ETAG_SYMLINK_HDR, None))
if etag and any(c in etag for c in ';"\\'): if etag and any(c in etag for c in ';"\\'):
# See cgi.parse_header for why the above chars are problematic # See cgi.parse_header for why the above chars are problematic
raise HTTPBadRequest( raise HTTPBadRequest(

View File

@ -1562,7 +1562,7 @@ class TestSymlinkSlo(Base):
self.env.container2.name, 'manifest-abcde'), self.env.container2.name, 'manifest-abcde'),
'X-Symlink-Target-Etag': slo_etag, 'X-Symlink-Target-Etag': slo_etag,
}) })
self.assert_status(400) # no quotes allowed! self.assert_status(409) # quotes OK, but doesn't match
# try the slo etag w/o the quotes # try the slo etag w/o the quotes
slo_etag = slo_etag.strip('"') slo_etag = slo_etag.strip('"')
@ -1571,7 +1571,7 @@ class TestSymlinkSlo(Base):
self.env.container2.name, 'manifest-abcde'), self.env.container2.name, 'manifest-abcde'),
'X-Symlink-Target-Etag': slo_etag, 'X-Symlink-Target-Etag': slo_etag,
}) })
self.assert_status(409) # that just doesn't match self.assert_status(409) # that still doesn't match
def test_static_link_target_symlink_to_slo_manifest(self): def test_static_link_target_symlink_to_slo_manifest(self):
# write symlink # write symlink

View File

@ -144,6 +144,33 @@ class TestSymlinkMiddleware(TestSymlinkMiddlewareBase):
self.assertEqual('application/foo', self.assertEqual('application/foo',
self.app._calls[-1].headers['Content-Type']) self.app._calls[-1].headers['Content-Type'])
def test_symlink_simple_put_with_quoted_etag(self):
self.app.register('HEAD', '/v1/a/c1/o', swob.HTTPOk, {
'Etag': 'tgt-etag', 'Content-Length': 42,
'Content-Type': 'application/foo'})
self.app.register('PUT', '/v1/a/c/symlink', swob.HTTPCreated, {})
req = Request.blank('/v1/a/c/symlink', method='PUT',
headers={
'X-Symlink-Target': 'c1/o',
'X-Symlink-Target-Etag': '"tgt-etag"',
}, body='')
status, headers, body = self.call_sym(req)
self.assertEqual(status, '201 Created')
method, path, hdrs = self.app.calls_with_headers[1]
val = hdrs.get('X-Object-Sysmeta-Symlink-Target')
self.assertEqual(val, 'c1/o')
self.assertNotIn('X-Object-Sysmeta-Symlink-Target-Account', hdrs)
val = hdrs.get('X-Object-Sysmeta-Container-Update-Override-Etag')
self.assertEqual(val, '%s; symlink_target=c1/o; '
'symlink_target_etag=tgt-etag; '
'symlink_target_bytes=42' % MD5_OF_EMPTY_STRING)
self.assertEqual([
('HEAD', '/v1/a/c1/o'),
('PUT', '/v1/a/c/symlink'),
], self.app.calls)
self.assertEqual('application/foo',
self.app._calls[-1].headers['Content-Type'])
def test_symlink_simple_put_with_etag_target_missing_content_type(self): def test_symlink_simple_put_with_etag_target_missing_content_type(self):
self.app.register('HEAD', '/v1/a/c1/o', swob.HTTPOk, { self.app.register('HEAD', '/v1/a/c1/o', swob.HTTPOk, {
'Etag': 'tgt-etag', 'Content-Length': 42}) 'Etag': 'tgt-etag', 'Content-Length': 42})
@ -1094,15 +1121,14 @@ class SymlinkCopyingTestCase(TestSymlinkMiddlewareBase):
}, body='') }, body='')
status, headers, body = self.call_sym(req) status, headers, body = self.call_sym(req)
self.assertEqual(status, '409 Conflict') self.assertEqual(status, '409 Conflict')
# the quoted slo-etag is just straight up invalid # the quoted slo-etag is tolerated, but still doesn't match
req = Request.blank('/v1/a/c/symlink', method='PUT', req = Request.blank('/v1/a/c/symlink', method='PUT',
headers={ headers={
'X-Symlink-Target': 'c1/o', 'X-Symlink-Target': 'c1/o',
'X-Symlink-Target-Etag': '"slo-etag"', 'X-Symlink-Target-Etag': '"slo-etag"',
}, body='') }, body='')
status, headers, body = self.call_sym(req) status, headers, body = self.call_sym(req)
self.assertEqual(status, '400 Bad Request') self.assertEqual(status, '409 Conflict')
self.assertEqual(b'Bad X-Symlink-Target-Etag format', body)
class SymlinkVersioningTestCase(TestSymlinkMiddlewareBase): class SymlinkVersioningTestCase(TestSymlinkMiddlewareBase):