diff --git a/swift/common/middleware/s3api/s3response.py b/swift/common/middleware/s3api/s3response.py index f74147095a..bc1aff7c0a 100644 --- a/swift/common/middleware/s3api/s3response.py +++ b/swift/common/middleware/s3api/s3response.py @@ -64,7 +64,7 @@ def translate_swift_to_s3(key, val): if _key.startswith('x-object-meta-'): return translate_meta_key(_key), val - elif _key in ('content-length', 'content-type', + elif _key in ('accept-ranges', 'content-length', 'content-type', 'content-range', 'content-encoding', 'content-disposition', 'content-language', 'etag', 'last-modified', 'x-robots-tag', diff --git a/test/functional/s3api/test_object.py b/test/functional/s3api/test_object.py index b6fee2d723..d3ceef8344 100644 --- a/test/functional/s3api/test_object.py +++ b/test/functional/s3api/test_object.py @@ -133,6 +133,7 @@ class TestS3ApiObject(S3ApiBase): self.assertTrue(headers['last-modified'] is not None) self.assertTrue(headers['content-type'] is not None) self.assertEqual(headers['content-length'], str(len(content))) + self.assertEqual(headers['accept-ranges'], 'bytes') # HEAD Object status, headers, body = \ @@ -143,6 +144,7 @@ class TestS3ApiObject(S3ApiBase): self.assertTrue(headers['last-modified'] is not None) self.assertTrue('content-type' in headers) self.assertEqual(headers['content-length'], str(len(content))) + self.assertEqual(headers['accept-ranges'], 'bytes') # DELETE Object status, headers, body = \ @@ -671,6 +673,7 @@ class TestS3ApiObject(S3ApiBase): self.assertEqual(status, 200) self.assertCommonResponseHeaders(headers) self.assertEqual(headers['content-type'], 'text/plain') + self.assertTrue('accept-ranges' in headers) def test_get_object_response_content_language(self): obj = 'object' @@ -682,6 +685,7 @@ class TestS3ApiObject(S3ApiBase): self.assertEqual(status, 200) self.assertCommonResponseHeaders(headers) self.assertEqual(headers['content-language'], 'en') + self.assertTrue('accept-ranges' in headers) def test_get_object_response_cache_control(self): obj = 'object' @@ -692,6 +696,7 @@ class TestS3ApiObject(S3ApiBase): self.conn.make_request('GET', self.bucket, obj, query=query) self.assertEqual(status, 200) self.assertCommonResponseHeaders(headers) + self.assertTrue('accept-ranges' in headers) self.assertEqual(headers['cache-control'], 'private') def test_get_object_response_content_disposition(self): @@ -704,6 +709,7 @@ class TestS3ApiObject(S3ApiBase): self.assertEqual(status, 200) self.assertCommonResponseHeaders(headers) self.assertEqual(headers['content-disposition'], 'inline') + self.assertTrue('accept-ranges' in headers) def test_get_object_response_content_encoding(self): obj = 'object' @@ -715,6 +721,7 @@ class TestS3ApiObject(S3ApiBase): self.assertEqual(status, 200) self.assertCommonResponseHeaders(headers) self.assertEqual(headers['content-encoding'], 'gzip') + self.assertTrue('accept-ranges' in headers) def test_get_object_range(self): obj = 'object' @@ -730,6 +737,7 @@ class TestS3ApiObject(S3ApiBase): self.assertEqual(status, 206) self.assertCommonResponseHeaders(headers) self.assertTrue('content-length' in headers) + self.assertTrue('accept-ranges' in headers) self.assertEqual(headers['content-length'], '5') self.assertTrue('x-amz-meta-test' in headers) self.assertEqual('swift', headers['x-amz-meta-test']) @@ -742,6 +750,7 @@ class TestS3ApiObject(S3ApiBase): self.assertEqual(status, 206) self.assertCommonResponseHeaders(headers) self.assertTrue('content-length' in headers) + self.assertTrue('accept-ranges' in headers) self.assertEqual(headers['content-length'], '5') self.assertTrue('x-amz-meta-test' in headers) self.assertEqual('swift', headers['x-amz-meta-test']) @@ -752,6 +761,7 @@ class TestS3ApiObject(S3ApiBase): self.conn.make_request('GET', self.bucket, obj, headers=headers) self.assertEqual(status, 206) self.assertCommonResponseHeaders(headers) + self.assertTrue('accept-ranges' in headers) self.assertTrue('content-length' in headers) self.assertEqual(headers['content-length'], '5') self.assertTrue('x-amz-meta-test' in headers) @@ -765,6 +775,7 @@ class TestS3ApiObject(S3ApiBase): self.conn.make_request('GET', self.bucket, obj, headers=headers) self.assertEqual(status, 206) self.assertCommonResponseHeaders(headers) + self.assertTrue('accept-ranges' in headers) self.assertIn('content-length', headers) self.assertIn('content-type', headers) # sanity @@ -816,6 +827,7 @@ class TestS3ApiObject(S3ApiBase): status, headers, body = \ self.conn.make_request('GET', self.bucket, obj, headers=headers) self.assertEqual(status, 200) + self.assertTrue('accept-ranges' in headers) self.assertCommonResponseHeaders(headers) def test_get_object_if_unmodified_since(self): @@ -831,7 +843,7 @@ class TestS3ApiObject(S3ApiBase): self.conn.make_request('GET', self.bucket, obj, headers=headers) self.assertEqual(status, 200) self.assertCommonResponseHeaders(headers) - + self.assertTrue('accept-ranges' in headers) # check we can use the last modified time from the listing... status, headers, body = \ self.conn.make_request('GET', self.bucket) @@ -847,11 +859,13 @@ class TestS3ApiObject(S3ApiBase): self.conn.make_request('GET', self.bucket, obj, headers=headers) self.assertEqual(status, 200) self.assertCommonResponseHeaders(headers) + self.assertTrue('accept-ranges' in headers) headers = {'If-Modified-Since': header_datetime} status, headers, body = \ self.conn.make_request('GET', self.bucket, obj, headers=headers) self.assertEqual(status, 304) + self.assertTrue('accept-ranges' in headers) self.assertCommonResponseHeaders(headers) def test_get_object_if_match(self): @@ -867,6 +881,7 @@ class TestS3ApiObject(S3ApiBase): self.conn.make_request('GET', self.bucket, obj, headers=headers) self.assertEqual(status, 200) self.assertCommonResponseHeaders(headers) + self.assertTrue('accept-ranges' in headers) def test_get_object_if_none_match(self): obj = 'object' @@ -876,6 +891,7 @@ class TestS3ApiObject(S3ApiBase): status, headers, body = \ self.conn.make_request('GET', self.bucket, obj, headers=headers) self.assertEqual(status, 200) + self.assertTrue('accept-ranges' in headers) self.assertCommonResponseHeaders(headers) def test_head_object_range(self): @@ -888,18 +904,21 @@ class TestS3ApiObject(S3ApiBase): self.conn.make_request('HEAD', self.bucket, obj, headers=headers) self.assertEqual(headers['content-length'], '5') self.assertCommonResponseHeaders(headers) + self.assertTrue('accept-ranges' in headers) headers = {'Range': 'bytes=5-'} status, headers, body = \ self.conn.make_request('HEAD', self.bucket, obj, headers=headers) self.assertEqual(headers['content-length'], '5') self.assertCommonResponseHeaders(headers) + self.assertTrue('accept-ranges' in headers) headers = {'Range': 'bytes=-5'} status, headers, body = \ self.conn.make_request('HEAD', self.bucket, obj, headers=headers) self.assertEqual(headers['content-length'], '5') self.assertCommonResponseHeaders(headers) + self.assertTrue('accept-ranges' in headers) def test_head_object_if_modified_since(self): obj = 'object' @@ -914,6 +933,7 @@ class TestS3ApiObject(S3ApiBase): self.conn.make_request('HEAD', self.bucket, obj, headers=headers) self.assertEqual(status, 200) self.assertCommonResponseHeaders(headers) + self.assertTrue('accept-ranges' in headers) def test_head_object_if_unmodified_since(self): obj = 'object' @@ -928,6 +948,7 @@ class TestS3ApiObject(S3ApiBase): self.conn.make_request('HEAD', self.bucket, obj, headers=headers) self.assertEqual(status, 200) self.assertCommonResponseHeaders(headers) + self.assertTrue('accept-ranges' in headers) def test_head_object_if_match(self): obj = 'object' @@ -942,6 +963,7 @@ class TestS3ApiObject(S3ApiBase): self.conn.make_request('HEAD', self.bucket, obj, headers=headers) self.assertEqual(status, 200) self.assertCommonResponseHeaders(headers) + self.assertTrue('accept-ranges' in headers) def test_head_object_if_none_match(self): obj = 'object' @@ -952,6 +974,7 @@ class TestS3ApiObject(S3ApiBase): self.conn.make_request('HEAD', self.bucket, obj, headers=headers) self.assertEqual(status, 200) self.assertCommonResponseHeaders(headers) + self.assertTrue('accept-ranges' in headers) class TestS3ApiObjectSigV4(TestS3ApiObject, SigV4Mixin): diff --git a/test/unit/common/middleware/s3api/test_obj.py b/test/unit/common/middleware/s3api/test_obj.py index 3b0907edb2..22c4bfb1c9 100644 --- a/test/unit/common/middleware/s3api/test_obj.py +++ b/test/unit/common/middleware/s3api/test_obj.py @@ -53,6 +53,7 @@ class TestS3ApiObj(S3ApiTestCase): self.response_headers = {'Content-Type': 'text/html', 'Content-Length': len(self.object_body), + 'Accept-Ranges': 'bytes', 'Content-Disposition': 'inline', 'Content-Language': 'en', 'x-object-meta-test': 'swift', @@ -89,7 +90,8 @@ class TestS3ApiObj(S3ApiTestCase): for key, val in self.response_headers.items(): if key in ('Content-Length', 'Content-Type', 'content-encoding', 'last-modified', 'cache-control', 'Content-Disposition', - 'Content-Language', 'expires', 'x-robots-tag'): + 'Content-Language', 'expires', 'x-robots-tag', + 'Accept-Ranges'): self.assertIn(key, headers) self.assertEqual(headers[key], str(val)) @@ -407,6 +409,8 @@ class TestS3ApiObj(S3ApiTestCase): self.assertTrue('cache-control' in headers) self.assertEqual(headers['cache-control'], 'no-cache') self.assertTrue('content-disposition' in headers) + self.assertTrue('accept-ranges' in headers) + self.assertEqual(headers['accept-ranges'], 'bytes') self.assertEqual(headers['content-disposition'], 'attachment') self.assertTrue('content-encoding' in headers) @@ -435,6 +439,8 @@ class TestS3ApiObj(S3ApiTestCase): status, headers, body = self.call_s3api(req) self.assertEqual(status.split()[0], '200', body) self.assertEqual(body, self.object_body) + self.assertTrue('accept-ranges' in headers) + self.assertEqual(headers['accept-ranges'], 'bytes') @s3acl def test_object_GET_version_id(self): @@ -455,6 +461,8 @@ class TestS3ApiObj(S3ApiTestCase): status, headers, body = self.call_s3api(req) self.assertEqual(status.split()[0], '200', body) self.assertEqual(body, self.object_body) + self.assertTrue('accept-ranges' in headers) + self.assertEqual(headers['accept-ranges'], 'bytes') # GET version in archive headers = self.response_headers.copy()