From e4d326b5a7dd186d762726faa45733ff2900343d Mon Sep 17 00:00:00 2001 From: Kazuhiro MIYAHARA Date: Thu, 19 Feb 2015 17:38:10 +0900 Subject: [PATCH] Fix conflict SLO reponse This patch fixes Swift to respond "409 Conflict" when a segment object path of the manifest on PUT SLO is same as requested object path. It is because the request will overwrite the segment and then it will absolutely cause "409 Conflict" on GET SLO. e.g.: request: PUT "http://hostname/v1/AUTH_account/container/segment_object_00?multipart-manifest=put" manifest file: [{"path" : "container/segment_object_00", "etag" : "", "size_bytes" : }, {"path" : "container/segment_object_01", "etag" : "", "size_bytes" : }, {"path" : "container/segment_object_02", "etag" : "", "size_bytes" : }] Change-Id: I4f4f7b9dbeb6a7c355b801c7e0ae560aa19a70b4 Closes-Bug: 1417936 --- AUTHORS | 1 + swift/common/middleware/slo.py | 5 +++ test/unit/common/middleware/test_slo.py | 52 ++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index be3c5deeb9..fa2cee7458 100644 --- a/AUTHORS +++ b/AUTHORS @@ -223,3 +223,4 @@ Hua Zhang (zhuadl@cn.ibm.com) Jian Zhang (jian.zhang@intel.com) Ning Zhang (ning@zmanda.com) Yuan Zhou (yuan.zhou@intel.com) +Kazuhiro Miyahara (miyahara.kazuhiro@lab.ntt.co.jp) diff --git a/swift/common/middleware/slo.py b/swift/common/middleware/slo.py index e8f1707e28..d8df829981 100644 --- a/swift/common/middleware/slo.py +++ b/swift/common/middleware/slo.py @@ -586,6 +586,11 @@ class StaticLargeObject(object): if isinstance(obj_name, unicode): obj_name = obj_name.encode('utf-8') obj_path = '/'.join(['', vrs, account, obj_name.lstrip('/')]) + if req.path == quote(obj_path): + raise HTTPConflict( + 'Manifest object name "%s" ' + 'cannot be included in the manifest' + % obj_name) try: seg_size = int(seg_dict['size_bytes']) except (ValueError, TypeError): diff --git a/test/unit/common/middleware/test_slo.py b/test/unit/common/middleware/test_slo.py index 4160d91d46..d70a25ccc4 100644 --- a/test/unit/common/middleware/test_slo.py +++ b/test/unit/common/middleware/test_slo.py @@ -24,7 +24,7 @@ from swift.common import swob, utils from swift.common.exceptions import ListingIterError, SegmentError from swift.common.middleware import slo from swift.common.swob import Request, Response, HTTPException -from swift.common.utils import json +from swift.common.utils import quote, json from test.unit.common.middleware.helpers import FakeSwift @@ -139,6 +139,11 @@ class TestSloPutManifest(SloTestCase): swob.HTTPOk, {'Content-Length': '100', 'Etag': 'etagoftheobjectsegment'}, None) + self.app.register( + 'HEAD', '/v1/AUTH_test/cont/object2', + swob.HTTPOk, + {'Content-Length': '100', 'Etag': 'etagoftheobjectsegment'}, + None) self.app.register( 'HEAD', '/v1/AUTH_test/cont/object\xe2\x99\xa1', swob.HTTPOk, @@ -149,6 +154,11 @@ class TestSloPutManifest(SloTestCase): swob.HTTPOk, {'Content-Length': '10', 'Etag': 'etagoftheobjectsegment'}, None) + self.app.register( + 'HEAD', u'/v1/AUTH_test/cont/あ_1', + swob.HTTPOk, + {'Content-Length': '1', 'Etag': 'a'}, + None) self.app.register( 'PUT', '/v1/AUTH_test/c/man', swob.HTTPCreated, {}, None) self.app.register( @@ -391,6 +401,46 @@ class TestSloPutManifest(SloTestCase): self.assertEquals(errors[4][0], '/checktest/slob') self.assertEquals(errors[4][1], 'Etag Mismatch') + def test_handle_multipart_put_manifest_equal_slo(self): + test_json_data = json.dumps([{'path': '/cont/object', + 'etag': 'etagoftheobjectsegment', + 'size_bytes': 100}]) + req = Request.blank( + '/v1/AUTH_test/cont/object?multipart-manifest=put', + environ={'REQUEST_METHOD': 'PUT'}, headers={'Accept': 'test'}, + body=test_json_data) + status, headers, body = self.call_slo(req) + self.assertEqual(status, '409 Conflict') + self.assertEqual(self.app.call_count, 0) + + def test_handle_multipart_put_manifest_equal_slo_non_ascii(self): + test_json_data = json.dumps([{'path': u'/cont/あ_1', + 'etag': 'a', + 'size_bytes': 1}]) + path = quote(u'/v1/AUTH_test/cont/あ_1') + req = Request.blank( + path + '?multipart-manifest=put', + environ={'REQUEST_METHOD': 'PUT'}, headers={'Accept': 'test'}, + body=test_json_data) + status, headers, body = self.call_slo(req) + self.assertEqual(status, '409 Conflict') + self.assertEqual(self.app.call_count, 0) + + def test_handle_multipart_put_manifest_equal_last_segment(self): + test_json_data = json.dumps([{'path': '/cont/object', + 'etag': 'etagoftheobjectsegment', + 'size_bytes': 100}, + {'path': '/cont/object2', + 'etag': 'etagoftheobjectsegment', + 'size_bytes': 100}]) + req = Request.blank( + '/v1/AUTH_test/cont/object2?multipart-manifest=put', + environ={'REQUEST_METHOD': 'PUT'}, headers={'Accept': 'test'}, + body=test_json_data) + status, headers, body = self.call_slo(req) + self.assertEqual(status, '409 Conflict') + self.assertEqual(self.app.call_count, 1) + class TestSloDeleteManifest(SloTestCase):