Merge "Properly handle custom metadata upon an object COPY operation"
This commit is contained in:
commit
9d9395b782
@ -1051,6 +1051,22 @@ class S3Request(swob.Request):
|
||||
env['HTTP_X_COPY_FROM'] = env['HTTP_X_AMZ_COPY_SOURCE']
|
||||
del env['HTTP_X_AMZ_COPY_SOURCE']
|
||||
env['CONTENT_LENGTH'] = '0'
|
||||
# Content type cannot be modified on COPY
|
||||
env.pop('CONTENT_TYPE', None)
|
||||
if env.pop('HTTP_X_AMZ_METADATA_DIRECTIVE', None) == 'REPLACE':
|
||||
env['HTTP_X_FRESH_METADATA'] = 'True'
|
||||
else:
|
||||
copy_exclude_headers = ('HTTP_CONTENT_DISPOSITION',
|
||||
'HTTP_CONTENT_ENCODING',
|
||||
'HTTP_CONTENT_LANGUAGE',
|
||||
'HTTP_EXPIRES',
|
||||
'HTTP_CACHE_CONTROL',
|
||||
'HTTP_X_ROBOTS_TAG')
|
||||
for key in copy_exclude_headers:
|
||||
env.pop(key, None)
|
||||
for key in list(env.keys()):
|
||||
if key.startswith('HTTP_X_OBJECT_META_'):
|
||||
del env[key]
|
||||
|
||||
if self.force_request_log:
|
||||
env['swift.proxy_access_log_made'] = False
|
||||
|
@ -487,6 +487,35 @@ class TestS3ApiObject(S3ApiBase):
|
||||
self.conn.make_request('HEAD', dst_bucket, dst_obj)
|
||||
self.assertEqual(headers['x-amz-meta-test'], 'dst')
|
||||
|
||||
headers = {'X-Amz-Copy-Source': '/%s/%s' % (self.bucket, obj),
|
||||
'X-Amz-Metadata-Directive': 'COPY',
|
||||
'X-Amz-Meta-Test': 'dst'}
|
||||
status, headers, body = \
|
||||
self.conn.make_request('PUT', dst_bucket, dst_obj, headers)
|
||||
self.assertEqual(status, 200)
|
||||
self.assertCommonResponseHeaders(headers)
|
||||
status, headers, body = \
|
||||
self.conn.make_request('HEAD', dst_bucket, dst_obj)
|
||||
self.assertEqual(headers['x-amz-meta-test'], 'src')
|
||||
|
||||
headers = {'X-Amz-Copy-Source': '/%s/%s' % (self.bucket, obj),
|
||||
'X-Amz-Meta-Test2': 'dst',
|
||||
'X-Amz-Metadata-Directive': 'REPLACE'}
|
||||
status, headers, body = \
|
||||
self.conn.make_request('PUT', dst_bucket, dst_obj, headers)
|
||||
self.assertEqual(status, 200)
|
||||
self.assertCommonResponseHeaders(headers)
|
||||
status, headers, body = \
|
||||
self.conn.make_request('HEAD', dst_bucket, dst_obj)
|
||||
self.assertNotIn('x-amz-meta-test', headers)
|
||||
self.assertEqual(headers['x-amz-meta-test2'], 'dst')
|
||||
|
||||
headers = {'X-Amz-Copy-Source': '/%s/%s' % (self.bucket, obj),
|
||||
'X-Amz-Metadata-Directive': 'BAD'}
|
||||
status, headers, body = \
|
||||
self.conn.make_request('PUT', dst_bucket, dst_obj, headers)
|
||||
self.assertEqual(status, 400)
|
||||
|
||||
def test_put_object_copy_source_if_modified_since(self):
|
||||
obj = 'object'
|
||||
dst_bucket = 'dst-bucket'
|
||||
|
@ -515,13 +515,12 @@ class TestS3ApiObj(S3ApiTestCase):
|
||||
_, _, headers = self.swift.calls_with_headers[-1]
|
||||
# Check that s3api converts a Content-MD5 header into an etag.
|
||||
self.assertEqual(headers['ETag'], self.etag)
|
||||
self.assertEqual(headers['X-Object-Meta-Something'], 'oh hai')
|
||||
self.assertEqual(headers['X-Object-Meta-Unreadable-Prefix'],
|
||||
'=?UTF-8?Q?=04w?=')
|
||||
self.assertEqual(headers['X-Object-Meta-Unreadable-Suffix'],
|
||||
'=?UTF-8?Q?h=04?=')
|
||||
self.assertEqual(headers['X-Object-Meta-Lots-Of-Unprintable'],
|
||||
'=?UTF-8?B?BAQEBAQ=?=')
|
||||
# Check that metadata is omited if no directive is specified
|
||||
self.assertIsNone(headers.get('X-Object-Meta-Something'))
|
||||
self.assertIsNone(headers.get('X-Object-Meta-Unreadable-Prefix'))
|
||||
self.assertIsNone(headers.get('X-Object-Meta-Unreadable-Suffix'))
|
||||
self.assertIsNone(headers.get('X-Object-Meta-Lots-Of-Unprintable'))
|
||||
|
||||
self.assertEqual(headers['X-Copy-From'], '/some/source')
|
||||
self.assertEqual(headers['Content-Length'], '0')
|
||||
|
||||
@ -588,6 +587,7 @@ class TestS3ApiObj(S3ApiTestCase):
|
||||
|
||||
_, _, headers = self.swift.calls_with_headers[-1]
|
||||
self.assertEqual(headers['X-Copy-From'], '/some/source')
|
||||
self.assertTrue(headers.get('X-Fresh-Metadata') is None)
|
||||
self.assertEqual(headers['Content-Length'], '0')
|
||||
|
||||
do_test('/some/source')
|
||||
@ -597,6 +597,104 @@ class TestS3ApiObj(S3ApiTestCase):
|
||||
# AWS seems to tolerate this so we should, too
|
||||
do_test('some/source')
|
||||
|
||||
@s3acl
|
||||
def test_object_PUT_copy_metadata_replace(self):
|
||||
date_header = self.get_date_header()
|
||||
timestamp = mktime(date_header)
|
||||
last_modified = S3Timestamp(timestamp).s3xmlformat
|
||||
status, headers, body = \
|
||||
self._test_object_PUT_copy(
|
||||
swob.HTTPOk,
|
||||
{'X-Amz-Metadata-Directive': 'REPLACE',
|
||||
'X-Amz-Meta-Something': 'oh hai',
|
||||
'X-Amz-Meta-Unreadable-Prefix': '\x04w',
|
||||
'X-Amz-Meta-Unreadable-Suffix': 'h\x04',
|
||||
'X-Amz-Meta-Lots-Of-Unprintable': 5 * '\x04',
|
||||
'Cache-Control': 'hello',
|
||||
'content-disposition': 'how are you',
|
||||
'content-encoding': 'good and you',
|
||||
'content-language': 'great',
|
||||
'content-type': 'so',
|
||||
'expires': 'yeah',
|
||||
'x-robots-tag': 'bye'})
|
||||
|
||||
self.assertEqual(status.split()[0], '200')
|
||||
self.assertEqual(headers['Content-Type'], 'application/xml')
|
||||
self.assertIsNone(headers.get('etag'))
|
||||
elem = fromstring(body, 'CopyObjectResult')
|
||||
self.assertEqual(elem.find('LastModified').text, last_modified)
|
||||
self.assertEqual(elem.find('ETag').text, '"%s"' % self.etag)
|
||||
|
||||
_, _, headers = self.swift.calls_with_headers[-1]
|
||||
self.assertEqual(headers['X-Copy-From'], '/some/source')
|
||||
# Check that metadata is included if replace directive is specified
|
||||
# and that Fresh Metadata is set
|
||||
self.assertTrue(headers.get('X-Fresh-Metadata') == 'True')
|
||||
self.assertEqual(headers['X-Object-Meta-Something'], 'oh hai')
|
||||
self.assertEqual(headers['X-Object-Meta-Unreadable-Prefix'],
|
||||
'=?UTF-8?Q?=04w?=')
|
||||
self.assertEqual(headers['X-Object-Meta-Unreadable-Suffix'],
|
||||
'=?UTF-8?Q?h=04?=')
|
||||
self.assertEqual(headers['X-Object-Meta-Lots-Of-Unprintable'],
|
||||
'=?UTF-8?B?BAQEBAQ=?=')
|
||||
# Check other metadata is set
|
||||
self.assertEqual(headers['Cache-Control'], 'hello')
|
||||
self.assertEqual(headers['Content-Disposition'], 'how are you')
|
||||
self.assertEqual(headers['Content-Encoding'], 'good and you')
|
||||
self.assertEqual(headers['Content-Language'], 'great')
|
||||
# Content-Type can't be set during an S3 copy operation
|
||||
self.assertIsNone(headers.get('Content-Type'))
|
||||
self.assertEqual(headers['Expires'], 'yeah')
|
||||
self.assertEqual(headers['X-Robots-Tag'], 'bye')
|
||||
|
||||
self.assertEqual(headers['Content-Length'], '0')
|
||||
|
||||
@s3acl
|
||||
def test_object_PUT_copy_metadata_copy(self):
|
||||
date_header = self.get_date_header()
|
||||
timestamp = mktime(date_header)
|
||||
last_modified = S3Timestamp(timestamp).s3xmlformat
|
||||
status, headers, body = \
|
||||
self._test_object_PUT_copy(
|
||||
swob.HTTPOk,
|
||||
{'X-Amz-Metadata-Directive': 'COPY',
|
||||
'X-Amz-Meta-Something': 'oh hai',
|
||||
'X-Amz-Meta-Unreadable-Prefix': '\x04w',
|
||||
'X-Amz-Meta-Unreadable-Suffix': 'h\x04',
|
||||
'X-Amz-Meta-Lots-Of-Unprintable': 5 * '\x04',
|
||||
'Cache-Control': 'hello',
|
||||
'content-disposition': 'how are you',
|
||||
'content-encoding': 'good and you',
|
||||
'content-language': 'great',
|
||||
'content-type': 'so',
|
||||
'expires': 'yeah',
|
||||
'x-robots-tag': 'bye'})
|
||||
self.assertEqual(status.split()[0], '200')
|
||||
self.assertEqual(headers['Content-Type'], 'application/xml')
|
||||
self.assertIsNone(headers.get('etag'))
|
||||
|
||||
elem = fromstring(body, 'CopyObjectResult')
|
||||
self.assertEqual(elem.find('LastModified').text, last_modified)
|
||||
self.assertEqual(elem.find('ETag').text, '"%s"' % self.etag)
|
||||
|
||||
_, _, headers = self.swift.calls_with_headers[-1]
|
||||
self.assertEqual(headers['X-Copy-From'], '/some/source')
|
||||
# Check that metadata is omited if COPY directive is specified
|
||||
self.assertIsNone(headers.get('X-Fresh-Metadata'))
|
||||
self.assertIsNone(headers.get('X-Object-Meta-Something'))
|
||||
self.assertIsNone(headers.get('X-Object-Meta-Unreadable-Prefix'))
|
||||
self.assertIsNone(headers.get('X-Object-Meta-Unreadable-Suffix'))
|
||||
self.assertIsNone(headers.get('X-Object-Meta-Lots-Of-Unprintable'))
|
||||
self.assertIsNone(headers.get('Cache-Control'))
|
||||
self.assertIsNone(headers.get('Content-Disposition'))
|
||||
self.assertIsNone(headers.get('Content-Encoding'))
|
||||
self.assertIsNone(headers.get('Content-Language'))
|
||||
self.assertIsNone(headers.get('Content-Type'))
|
||||
self.assertIsNone(headers.get('Expires'))
|
||||
self.assertIsNone(headers.get('X-Robots-Tag'))
|
||||
|
||||
self.assertEqual(headers['Content-Length'], '0')
|
||||
|
||||
@s3acl
|
||||
def test_object_PUT_copy_self(self):
|
||||
status, headers, body = \
|
||||
|
Loading…
Reference in New Issue
Block a user