diff --git a/swift/common/middleware/listing_formats.py b/swift/common/middleware/listing_formats.py index 7803462679..d70ac484e5 100644 --- a/swift/common/middleware/listing_formats.py +++ b/swift/common/middleware/listing_formats.py @@ -55,12 +55,23 @@ def get_listing_content_type(req): out_content_type = req.accept.best_match( ['text/plain', 'application/json', 'application/xml', 'text/xml']) except ValueError: - raise HTTPBadRequest(request=req, body='Invalid Accept header') + raise HTTPBadRequest(request=req, body=b'Invalid Accept header') if not out_content_type: raise HTTPNotAcceptable(request=req) return out_content_type +def to_xml(document_element): + result = tostring(document_element, encoding='UTF-8').replace( + b"", + b'', 1) + if not result.startswith(b'\n' + result + return result + + def account_to_xml(listing, account_name): if isinstance(account_name, bytes): account_name = account_name.decode('utf-8') @@ -76,9 +87,7 @@ def account_to_xml(listing, account_name): SubElement(sub, field).text = six.text_type( record.pop(field)) sub.tail = '\n' - return tostring(doc, encoding='UTF-8').replace( - b"", - b'', 1) + return to_xml(doc) def container_to_xml(listing, base_name): @@ -96,10 +105,7 @@ def container_to_xml(listing, base_name): 'last_modified'): SubElement(sub, field).text = six.text_type( record.pop(field)) - - return tostring(doc, encoding='UTF-8').replace( - b"", - b'', 1) + return to_xml(doc) def listing_to_text(listing): diff --git a/test/unit/common/middleware/test_listing_formats.py b/test/unit/common/middleware/test_listing_formats.py index d140026b8f..6ac6d08735 100644 --- a/test/unit/common/middleware/test_listing_formats.py +++ b/test/unit/common/middleware/test_listing_formats.py @@ -29,13 +29,13 @@ class TestListingFormats(unittest.TestCase): {'name': 'bar', 'bytes': 0, 'count': 0, 'last_modified': '1970-01-01T00:00:00.000000'}, {'subdir': 'foo_'}, - ]) + ]).encode('ascii') self.fake_container_listing = json.dumps([ {'name': 'bar', 'hash': 'etag', 'bytes': 0, 'content_type': 'text/plain', 'last_modified': '1970-01-01T00:00:00.000000'}, {'subdir': 'foo/'}, - ]) + ]).encode('ascii') def test_valid_account(self): self.fake_swift.register('GET', '/v1/a', HTTPOk, { @@ -44,7 +44,7 @@ class TestListingFormats(unittest.TestCase): req = Request.blank('/v1/a') resp = req.get_response(self.app) - self.assertEqual(resp.body, 'bar\nfoo_\n') + self.assertEqual(resp.body, b'bar\nfoo_\n') self.assertEqual(resp.headers['Content-Type'], 'text/plain; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( @@ -52,7 +52,7 @@ class TestListingFormats(unittest.TestCase): req = Request.blank('/v1/a?format=txt') resp = req.get_response(self.app) - self.assertEqual(resp.body, 'bar\nfoo_\n') + self.assertEqual(resp.body, b'bar\nfoo_\n') self.assertEqual(resp.headers['Content-Type'], 'text/plain; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( @@ -68,14 +68,14 @@ class TestListingFormats(unittest.TestCase): req = Request.blank('/v1/a?format=xml') resp = req.get_response(self.app) - self.assertEqual(resp.body.split('\n'), [ - '', - '', - 'bar00' - '1970-01-01T00:00:00.000000' - '', - '', - '', + self.assertEqual(resp.body.split(b'\n'), [ + b'', + b'', + b'bar00' + b'1970-01-01T00:00:00.000000' + b'', + b'', + b'', ]) self.assertEqual(resp.headers['Content-Type'], 'application/xml; charset=utf-8') @@ -89,7 +89,7 @@ class TestListingFormats(unittest.TestCase): req = Request.blank('/v1/a/c') resp = req.get_response(self.app) - self.assertEqual(resp.body, 'bar\nfoo/\n') + self.assertEqual(resp.body, b'bar\nfoo/\n') self.assertEqual(resp.headers['Content-Type'], 'text/plain; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( @@ -97,7 +97,7 @@ class TestListingFormats(unittest.TestCase): req = Request.blank('/v1/a/c?format=txt') resp = req.get_response(self.app) - self.assertEqual(resp.body, 'bar\nfoo/\n') + self.assertEqual(resp.body, b'bar\nfoo/\n') self.assertEqual(resp.headers['Content-Type'], 'text/plain; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( @@ -115,14 +115,14 @@ class TestListingFormats(unittest.TestCase): resp = req.get_response(self.app) self.assertEqual( resp.body, - '\n' - '' - 'baretag0' - 'text/plain' - '1970-01-01T00:00:00.000000' - '' - 'foo/' - '' + b'\n' + b'' + b'baretag0' + b'text/plain' + b'1970-01-01T00:00:00.000000' + b'' + b'foo/' + b'' ) self.assertEqual(resp.headers['Content-Type'], 'application/xml; charset=utf-8') @@ -131,12 +131,12 @@ class TestListingFormats(unittest.TestCase): def test_blank_account(self): self.fake_swift.register('GET', '/v1/a', HTTPOk, { - 'Content-Length': '2', 'Content-Type': 'application/json'}, '[]') + 'Content-Length': '2', 'Content-Type': 'application/json'}, b'[]') req = Request.blank('/v1/a') resp = req.get_response(self.app) self.assertEqual(resp.status, '204 No Content') - self.assertEqual(resp.body, '') + self.assertEqual(resp.body, b'') self.assertEqual(resp.headers['Content-Type'], 'text/plain; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( @@ -145,7 +145,7 @@ class TestListingFormats(unittest.TestCase): req = Request.blank('/v1/a?format=txt') resp = req.get_response(self.app) self.assertEqual(resp.status, '204 No Content') - self.assertEqual(resp.body, '') + self.assertEqual(resp.body, b'') self.assertEqual(resp.headers['Content-Type'], 'text/plain; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( @@ -154,7 +154,7 @@ class TestListingFormats(unittest.TestCase): req = Request.blank('/v1/a?format=json') resp = req.get_response(self.app) self.assertEqual(resp.status, '200 OK') - self.assertEqual(resp.body, '[]') + self.assertEqual(resp.body, b'[]') self.assertEqual(resp.headers['Content-Type'], 'application/json; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( @@ -163,10 +163,10 @@ class TestListingFormats(unittest.TestCase): req = Request.blank('/v1/a?format=xml') resp = req.get_response(self.app) self.assertEqual(resp.status, '200 OK') - self.assertEqual(resp.body.split('\n'), [ - '', - '', - '', + self.assertEqual(resp.body.split(b'\n'), [ + b'', + b'', + b'', ]) self.assertEqual(resp.headers['Content-Type'], 'application/xml; charset=utf-8') @@ -175,12 +175,12 @@ class TestListingFormats(unittest.TestCase): def test_blank_container(self): self.fake_swift.register('GET', '/v1/a/c', HTTPOk, { - 'Content-Length': '2', 'Content-Type': 'application/json'}, '[]') + 'Content-Length': '2', 'Content-Type': 'application/json'}, b'[]') req = Request.blank('/v1/a/c') resp = req.get_response(self.app) self.assertEqual(resp.status, '204 No Content') - self.assertEqual(resp.body, '') + self.assertEqual(resp.body, b'') self.assertEqual(resp.headers['Content-Type'], 'text/plain; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( @@ -189,7 +189,7 @@ class TestListingFormats(unittest.TestCase): req = Request.blank('/v1/a/c?format=txt') resp = req.get_response(self.app) self.assertEqual(resp.status, '204 No Content') - self.assertEqual(resp.body, '') + self.assertEqual(resp.body, b'') self.assertEqual(resp.headers['Content-Type'], 'text/plain; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( @@ -198,7 +198,7 @@ class TestListingFormats(unittest.TestCase): req = Request.blank('/v1/a/c?format=json') resp = req.get_response(self.app) self.assertEqual(resp.status, '200 OK') - self.assertEqual(resp.body, '[]') + self.assertEqual(resp.body, b'[]') self.assertEqual(resp.headers['Content-Type'], 'application/json; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( @@ -207,9 +207,9 @@ class TestListingFormats(unittest.TestCase): req = Request.blank('/v1/a/c?format=xml') resp = req.get_response(self.app) self.assertEqual(resp.status, '200 OK') - self.assertEqual(resp.body.split('\n'), [ - '', - '', + self.assertEqual(resp.body.split(b'\n'), [ + b'', + b'', ]) self.assertEqual(resp.headers['Content-Type'], 'application/xml; charset=utf-8') @@ -236,7 +236,7 @@ class TestListingFormats(unittest.TestCase): do_test('/v1/a/c/o') def test_static_web_not_json(self): - body = 'doesnt matter' + body = b'doesnt matter' self.fake_swift.register( 'GET', '/v1/staticweb/not-json', HTTPOk, {'Content-Length': str(len(body)), @@ -253,7 +253,7 @@ class TestListingFormats(unittest.TestCase): # FakeSwift seems to make this hard to do def test_static_web_not_really_json(self): - body = 'raises ValueError' + body = b'raises ValueError' self.fake_swift.register( 'GET', '/v1/staticweb/not-json', HTTPOk, {'Content-Length': str(len(body)), @@ -272,7 +272,7 @@ class TestListingFormats(unittest.TestCase): 'content_type': 'text/plain', 'last_modified': '1970-01-01T00:00:00.000000'}, {'subdir': 'foo/'}, - ] * 160000) + ] * 160000).encode('ascii') self.assertGreater( # sanity len(body), listing_formats.MAX_CONTAINER_LISTING_CONTENT_LENGTH) @@ -291,7 +291,7 @@ class TestListingFormats(unittest.TestCase): def test_static_web_bad_json(self): def do_test(body_obj): - body = json.dumps(body_obj) + body = json.dumps(body_obj).encode('ascii') self.fake_swift.register( 'GET', '/v1/staticweb/bad-json', HTTPOk, {'Content-Length': str(len(body)), @@ -322,7 +322,7 @@ class TestListingFormats(unittest.TestCase): do_test(['some string']) def test_static_web_bad_but_not_terrible_json(self): - body = json.dumps([{'no name': 'nor subdir'}]) + body = json.dumps([{'no name': 'nor subdir'}]).encode('ascii') self.fake_swift.register( 'GET', '/v1/staticweb/bad-json', HTTPOk, {'Content-Length': str(len(body)), diff --git a/tox.ini b/tox.ini index bec2e7cf85..b562252c11 100644 --- a/tox.ini +++ b/tox.ini @@ -42,6 +42,7 @@ commands = test/unit/common/middleware/test_gatekeeper.py \ test/unit/common/middleware/test_healthcheck.py \ test/unit/common/middleware/test_list_endpoints.py \ + test/unit/common/middleware/test_listing_formats.py \ test/unit/common/middleware/test_proxy_logging.py \ test/unit/common/ring \ test/unit/common/test_base_storage_server.py \