diff --git a/swift/account/server.py b/swift/account/server.py index d51f9f27a6..fd839a4909 100644 --- a/swift/account/server.py +++ b/swift/account/server.py @@ -244,7 +244,7 @@ class AccountController(object): account_list = '\n'.join(r[0] for r in account_list) + '\n' ret = Response(body=account_list, request=req, headers=resp_headers) ret.content_type = out_content_type - ret.charset = 'utf8' + ret.charset = 'utf-8' return ret def REPLICATE(self, req): diff --git a/swift/container/server.py b/swift/container/server.py index 75f7c2a350..39e3d4ac11 100644 --- a/swift/container/server.py +++ b/swift/container/server.py @@ -346,7 +346,7 @@ class ContainerController(object): container_list = '\n'.join(r[0] for r in container_list) + '\n' ret = Response(body=container_list, request=req, headers=resp_headers) ret.content_type = out_content_type - ret.charset = 'utf8' + ret.charset = 'utf-8' return ret def REPLICATE(self, req): diff --git a/swift/proxy/server.py b/swift/proxy/server.py index 32cd67ccb1..9be91cbc82 100644 --- a/swift/proxy/server.py +++ b/swift/proxy/server.py @@ -1368,8 +1368,26 @@ class AccountController(Controller): def GETorHEAD(self, req): """Handler for HTTP GET/HEAD requests.""" partition, nodes = self.app.account_ring.get_nodes(self.account_name) - return self.GETorHEAD_base(req, _('Account'), partition, nodes, + resp = self.GETorHEAD_base(req, _('Account'), partition, nodes, req.path_info.rstrip('/'), self.app.account_ring.replica_count) + if resp.status_int == 404 and self.app.account_autocreate: + if len(self.account_name) > MAX_ACCOUNT_NAME_LENGTH: + resp = HTTPBadRequest(request=req) + resp.body = 'Account name length of %d longer than %d' % \ + (len(self.account_name), MAX_ACCOUNT_NAME_LENGTH) + return resp + headers = {'X-Timestamp': normalize_timestamp(time.time()), + 'X-Trans-Id': self.trans_id} + resp = self.make_requests( + Request.blank('/v1/' + self.account_name), + self.app.account_ring, partition, 'PUT', + '/' + self.account_name, [headers] * len(nodes)) + if resp.status_int // 100 != 2: + raise Exception('Could not autocreate account %r' % + self.account_name) + resp = self.GETorHEAD_base(req, _('Account'), partition, nodes, + req.path_info.rstrip('/'), self.app.account_ring.replica_count) + return resp @public def PUT(self, req): @@ -1409,9 +1427,23 @@ class AccountController(Controller): if value[0].lower().startswith('x-account-meta-')) if self.app.memcache: self.app.memcache.delete('account%s' % req.path_info.rstrip('/')) - return self.make_requests(req, self.app.account_ring, + resp = self.make_requests(req, self.app.account_ring, account_partition, 'POST', req.path_info, [headers] * len(accounts)) + if resp.status_int == 404 and self.app.account_autocreate: + if len(self.account_name) > MAX_ACCOUNT_NAME_LENGTH: + resp = HTTPBadRequest(request=req) + resp.body = 'Account name length of %d longer than %d' % \ + (len(self.account_name), MAX_ACCOUNT_NAME_LENGTH) + return resp + resp = self.make_requests( + Request.blank('/v1/' + self.account_name), + self.app.account_ring, account_partition, 'PUT', + '/' + self.account_name, [headers] * len(accounts)) + if resp.status_int // 100 != 2: + raise Exception('Could not autocreate account %r' % + self.account_name) + return resp @public def DELETE(self, req): diff --git a/test/functional/tests.py b/test/functional/tests.py index 59dcf38960..8c513490d2 100644 --- a/test/functional/tests.py +++ b/test/functional/tests.py @@ -227,10 +227,10 @@ class TestAccount(Base): headers = dict(self.env.conn.response.getheaders()) if format == 'json': self.assertEquals(headers['content-type'], - 'application/json; charset=utf8') + 'application/json; charset=utf-8') elif format == 'xml': self.assertEquals(headers['content-type'], - 'application/xml; charset=utf8') + 'application/xml; charset=utf-8') def testListingLimit(self): limit = 10000 @@ -1355,10 +1355,10 @@ class TestFile(Base): headers = dict(self.env.conn.response.getheaders()) if format == 'json': self.assertEquals(headers['content-type'], - 'application/json; charset=utf8') + 'application/json; charset=utf-8') elif format == 'xml': self.assertEquals(headers['content-type'], - 'application/xml; charset=utf8') + 'application/xml; charset=utf-8') lm_diff = max([f['last_modified'] for f in files]) - \ min([f['last_modified'] for f in files]) diff --git a/test/unit/account/test_server.py b/test/unit/account/test_server.py index 16800ca165..238b7f3d18 100644 --- a/test/unit/account/test_server.py +++ b/test/unit/account/test_server.py @@ -388,6 +388,7 @@ class TestAccountController(unittest.TestCase): self.assertEquals(resp.status_int, 200) self.assertEquals(resp.body.strip().split('\n'), ['c1', 'c2']) self.assertEquals(resp.content_type, 'text/plain') + self.assertEquals(resp.charset, 'utf-8') def test_GET_with_containers_json(self): req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'PUT', @@ -436,6 +437,7 @@ class TestAccountController(unittest.TestCase): [{'count': 1, 'bytes': 2, 'name': 'c1'}, {'count': 3, 'bytes': 4, 'name': 'c2'}]) self.assertEquals(resp.content_type, 'application/json') + self.assertEquals(resp.charset, 'utf-8') def test_GET_with_containers_xml(self): req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'PUT', @@ -529,6 +531,7 @@ class TestAccountController(unittest.TestCase): self.assertEquals(node.firstChild.nodeValue, '3') node = [n for n in container if n.nodeName == 'bytes'][0] self.assertEquals(node.firstChild.nodeValue, '4') + self.assertEquals(resp.charset, 'utf-8') def test_GET_limit_marker_plain(self): req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'PUT', diff --git a/test/unit/common/middleware/test_staticweb.py b/test/unit/common/middleware/test_staticweb.py index 7b5385da71..55ff3959fa 100644 --- a/test/unit/common/middleware/test_staticweb.py +++ b/test/unit/common/middleware/test_staticweb.py @@ -187,7 +187,7 @@ class FakeApp(object): headers.update({'X-Container-Object-Count': '11', 'X-Container-Bytes-Used': '73741', 'X-Container-Read': '.r:*', - 'Content-Type': 'application/json; charset=utf8'}) + 'Content-Type': 'application/json; charset=utf-8'}) body = ''' [{"name":"subdir/1.txt", "hash":"5f595114a4b3077edfac792c61ca4fe4", "bytes":20, @@ -204,14 +204,14 @@ class FakeApp(object): headers.update({'X-Container-Object-Count': '11', 'X-Container-Bytes-Used': '73741', 'X-Container-Read': '.r:*', - 'Content-Type': 'application/json; charset=utf8'}) + 'Content-Type': 'application/json; charset=utf-8'}) body = '[]' elif env['PATH_INFO'] == '/v1/a/c3' and env['QUERY_STRING'] == \ 'limit=1&format=json&delimiter=/&limit=1&prefix=subdirz/': headers.update({'X-Container-Object-Count': '11', 'X-Container-Bytes-Used': '73741', 'X-Container-Read': '.r:*', - 'Content-Type': 'application/json; charset=utf8'}) + 'Content-Type': 'application/json; charset=utf-8'}) body = ''' [{"name":"subdirz/1.txt", "hash":"5f595114a4b3077edfac792c61ca4fe4", "bytes":20, @@ -224,7 +224,7 @@ class FakeApp(object): 'X-Container-Bytes-Used': '73741', 'X-Container-Read': '.r:*', 'X-Container-Web-Listings': 't', - 'Content-Type': 'application/json; charset=utf8'}) + 'Content-Type': 'application/json; charset=utf-8'}) body = ''' [{"name":"subdir/1.txt", "hash":"5f595114a4b3077edfac792c61ca4fe4", "bytes":20, @@ -236,7 +236,7 @@ class FakeApp(object): elif 'format=json' in env['QUERY_STRING']: headers.update({'X-Container-Object-Count': '11', 'X-Container-Bytes-Used': '73741', - 'Content-Type': 'application/json; charset=utf8'}) + 'Content-Type': 'application/json; charset=utf-8'}) body = ''' [{"name":"401error.html", "hash":"893f8d80692a4d3875b45be8f152ad18", "bytes":110, @@ -283,7 +283,7 @@ class FakeApp(object): else: headers.update({'X-Container-Object-Count': '11', 'X-Container-Bytes-Used': '73741', - 'Content-Type': 'text/plain; charset=utf8'}) + 'Content-Type': 'text/plain; charset=utf-8'}) body = '\n'.join(['401error.html', '404error.html', 'index.html', 'listing.css', 'one.txt', 'subdir/1.txt', 'subdir/2.txt', 'subdir/omgomg.txt', 'subdir2', diff --git a/test/unit/container/test_server.py b/test/unit/container/test_server.py index 5127fe093c..117a0dccd5 100644 --- a/test/unit/container/test_server.py +++ b/test/unit/container/test_server.py @@ -514,6 +514,7 @@ class TestContainerController(unittest.TestCase): resp = self.controller.GET(req) self.assertEquals(resp.content_type, 'application/json') self.assertEquals(eval(resp.body), json_body) + self.assertEquals(resp.charset, 'utf-8') for accept in ('application/json', 'application/json;q=1.0,*/*;q=0.9', '*/*;q=0.9,application/json;q=1.0', 'application/*'): @@ -552,6 +553,7 @@ class TestContainerController(unittest.TestCase): resp = self.controller.GET(req) self.assertEquals(resp.content_type, 'text/plain') self.assertEquals(resp.body, plain_body) + self.assertEquals(resp.charset, 'utf-8') for accept in ('', 'text/plain', 'application/xml;q=0.8,*/*;q=0.9', '*/*;q=0.9,application/xml;q=0.8', '*/*', @@ -609,6 +611,7 @@ class TestContainerController(unittest.TestCase): resp = self.controller.GET(req) self.assertEquals(resp.content_type, 'application/xml') self.assertEquals(resp.body, xml_body) + self.assertEquals(resp.charset, 'utf-8') for xml_accept in ('application/xml', 'application/xml;q=1.0,*/*;q=0.9', '*/*;q=0.9,application/xml;q=1.0', 'application/xml,text/xml'): diff --git a/test/unit/proxy/test_server.py b/test/unit/proxy/test_server.py index eb09ad34dc..b525ea9037 100644 --- a/test/unit/proxy/test_server.py +++ b/test/unit/proxy/test_server.py @@ -3171,6 +3171,16 @@ class TestAccountController(unittest.TestCase): self.app.memcache = FakeMemcacheReturnsNone() self.assert_status_map(controller.GET, (404, 404, 404), 404) + def test_GET_autocreate(self): + with save_globals(): + controller = proxy_server.AccountController(self.app, 'account') + self.app.memcache = FakeMemcacheReturnsNone() + self.assert_status_map(controller.GET, + (404, 404, 404, 201, 201, 201, 204), 404) + controller.app.account_autocreate = True + self.assert_status_map(controller.GET, + (404, 404, 404, 201, 201, 201, 204), 204) + def test_HEAD(self): with save_globals(): controller = proxy_server.AccountController(self.app, 'account') @@ -3189,6 +3199,26 @@ class TestAccountController(unittest.TestCase): self.assert_status_map(controller.HEAD, (404, 503, 503), 503) self.assert_status_map(controller.HEAD, (404, 204, 503), 204) + def test_HEAD_autocreate(self): + with save_globals(): + controller = proxy_server.AccountController(self.app, 'account') + self.app.memcache = FakeMemcacheReturnsNone() + self.assert_status_map(controller.HEAD, + (404, 404, 404, 201, 201, 201, 204), 404) + controller.app.account_autocreate = True + self.assert_status_map(controller.HEAD, + (404, 404, 404, 201, 201, 201, 204), 204) + + def test_POST_autocreate(self): + with save_globals(): + controller = proxy_server.AccountController(self.app, 'account') + self.app.memcache = FakeMemcacheReturnsNone() + self.assert_status_map(controller.POST, + (404, 404, 404, 201, 201, 201), 404) + controller.app.account_autocreate = True + self.assert_status_map(controller.POST, + (404, 404, 404, 201, 201, 201), 201) + def test_connection_refused(self): self.app.account_ring.get_nodes('account') for dev in self.app.account_ring.devs.values():