Merge "Allow clients to send quoted ETags for static links"
This commit is contained in:
commit
2b7e80217d
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in New Issue