From 0aad95005d03651da79071dd24354f0a54122ab6 Mon Sep 17 00:00:00 2001 From: Timur Alperovich Date: Wed, 23 May 2018 16:19:50 -0700 Subject: [PATCH] Fix SLO delete for accounts with non-ASCII names. If an account contains non-ASCII characters, currently SLO delete code will fail, as get_slo_segments() method receives a unicode object, but UTF-8 encoded account name. Attempting to concatenate the strings fails with a UnicodeError, as it tries to use the ASCII codec to decode the UTF-8 encoded account name. This patch allows accounts with non-ASCII characters in their names to delete SLOs. Change-Id: I619d41e62c16b25bd5f58d300a3dc71aa4dc75c2 --- swift/common/middleware/slo.py | 4 +-- test/unit/common/middleware/test_slo.py | 40 +++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/swift/common/middleware/slo.py b/swift/common/middleware/slo.py index 78c5f0835a..f23ba8a3ce 100644 --- a/swift/common/middleware/slo.py +++ b/swift/common/middleware/slo.py @@ -1367,8 +1367,8 @@ class StaticLargeObject(object): '%s MultipartDELETE' % new_env.get('HTTP_USER_AGENT') new_env['swift.source'] = 'SLO' new_env['PATH_INFO'] = ( - '/%s/%s/%s' % (vrs, account, obj_name.lstrip('/')) - ).encode('utf-8') + '/%s/%s/%s' % (vrs, account, obj_name.lstrip('/').encode('utf-8')) + ) resp = Request.blank('', new_env).get_response(self.app) if resp.is_success: diff --git a/test/unit/common/middleware/test_slo.py b/test/unit/common/middleware/test_slo.py index 0bb9626311..df880897f1 100644 --- a/test/unit/common/middleware/test_slo.py +++ b/test/unit/common/middleware/test_slo.py @@ -1038,6 +1038,12 @@ class TestSloDeleteManifest(SloTestCase): 'X-Static-Large-Object': 'true'}, json.dumps([{'name': '/deltest/b_2', 'hash': 'a', 'bytes': '1'}, {'name': '/deltest/c_3', 'hash': 'b', 'bytes': '2'}])) + self.app.register( + 'GET', '/v1/AUTH_test-un\xc3\xafcode/deltest/man-all-there', + swob.HTTPOk, {'Content-Type': 'application/json', + 'X-Static-Large-Object': 'true'}, + json.dumps([{'name': '/deltest/b_2', 'hash': 'a', 'bytes': '1'}, + {'name': '/deltest/c_3', 'hash': 'b', 'bytes': '2'}])) self.app.register( 'DELETE', '/v1/AUTH_test/deltest/man-all-there', swob.HTTPNoContent, {}, None) @@ -1059,6 +1065,15 @@ class TestSloDeleteManifest(SloTestCase): self.app.register( 'DELETE', '/v1/AUTH_test/deltest/d_3', swob.HTTPNoContent, {}, None) + self.app.register( + 'DELETE', '/v1/AUTH_test-un\xc3\xafcode/deltest/man-all-there', + swob.HTTPNoContent, {}, None) + self.app.register( + 'DELETE', '/v1/AUTH_test-un\xc3\xafcode/deltest/b_2', + swob.HTTPNoContent, {}, None) + self.app.register( + 'DELETE', '/v1/AUTH_test-un\xc3\xafcode/deltest/c_3', + swob.HTTPNoContent, {}, None) self.app.register( 'GET', '/v1/AUTH_test/deltest/manifest-with-submanifest', @@ -1190,6 +1205,31 @@ class TestSloDeleteManifest(SloTestCase): ('DELETE', ('/v1/AUTH_test/deltest/' + 'man-all-there?multipart-manifest=delete'))])) + def test_handle_multipart_delete_non_ascii(self): + acct = u'AUTH_test-un\u00efcode'.encode('utf-8') + req = Request.blank( + '/v1/%s/deltest/man-all-there?multipart-manifest=delete' % acct, + environ={'REQUEST_METHOD': 'DELETE'}) + status, _, body = self.call_slo(req) + self.assertEqual('200 OK', status) + lines = body.split('\n') + for l in lines: + parts = l.split(':') + if len(parts) == 1: + continue + key, value = parts + if key == 'Response Status': + delete_status = int(value.split()[0]) + self.assertEqual(200, delete_status) + + self.assertEqual(set(self.app.calls), set([ + ('GET', + '/v1/%s/deltest/man-all-there?multipart-manifest=get' % acct), + ('DELETE', '/v1/%s/deltest/b_2?multipart-manifest=delete' % acct), + ('DELETE', '/v1/%s/deltest/c_3?multipart-manifest=delete' % acct), + ('DELETE', ('/v1/%s/deltest/' + 'man-all-there?multipart-manifest=delete' % acct))])) + def test_handle_multipart_delete_nested(self): req = Request.blank( '/v1/AUTH_test/deltest/manifest-with-submanifest?' +