diff --git a/swift/account/server.py b/swift/account/server.py index 757fa6d904..ef469b0a60 100644 --- a/swift/account/server.py +++ b/swift/account/server.py @@ -23,21 +23,20 @@ from gettext import gettext as _ from eventlet import Timeout import swift.common.db -from swift.account.utils import account_listing_response, \ - account_listing_content_type +from swift.account.utils import account_listing_response from swift.common.db import AccountBroker, DatabaseConnectionError -from swift.common.request_helpers import get_param +from swift.common.request_helpers import get_param, get_listing_content_type from swift.common.utils import get_logger, hash_path, public, \ normalize_timestamp, storage_directory, config_true_value, \ validate_device_partition, json, timing_stats, replication from swift.common.constraints import ACCOUNT_LISTING_LIMIT, \ - check_mount, check_float, check_utf8, FORMAT2CONTENT_TYPE + check_mount, check_float, check_utf8 from swift.common.db_replicator import ReplicatorRpc from swift.common.swob import HTTPAccepted, HTTPBadRequest, \ HTTPCreated, HTTPForbidden, HTTPInternalServerError, \ HTTPMethodNotAllowed, HTTPNoContent, HTTPNotFound, \ HTTPPreconditionFailed, HTTPConflict, Request, \ - HTTPInsufficientStorage, HTTPNotAcceptable, HTTPException + HTTPInsufficientStorage, HTTPException DATADIR = 'accounts' @@ -181,14 +180,7 @@ class AccountController(object): except ValueError, err: return HTTPBadRequest(body=str(err), content_type='text/plain', request=req) - query_format = get_param(req, 'format') - if query_format: - req.accept = FORMAT2CONTENT_TYPE.get( - query_format.lower(), FORMAT2CONTENT_TYPE['plain']) - out_content_type = req.accept.best_match( - ['text/plain', 'application/json', 'application/xml', 'text/xml']) - if not out_content_type: - return HTTPNotAcceptable(request=req) + out_content_type = get_listing_content_type(req) if self.mount_check and not check_mount(self.root, drive): return HTTPInsufficientStorage(drive=drive, request=req) broker = self._get_account_broker(drive, part, account, @@ -234,9 +226,7 @@ class AccountController(object): ACCOUNT_LISTING_LIMIT) marker = get_param(req, 'marker', '') end_marker = get_param(req, 'end_marker') - out_content_type, error = account_listing_content_type(req) - if error: - return error + out_content_type = get_listing_content_type(req) if self.mount_check and not check_mount(self.root, drive): return HTTPInsufficientStorage(drive=drive, request=req) diff --git a/swift/account/utils.py b/swift/account/utils.py index e4c3b053b8..5565b46166 100644 --- a/swift/account/utils.py +++ b/swift/account/utils.py @@ -16,9 +16,7 @@ import time from xml.sax import saxutils -from swift.common.constraints import FORMAT2CONTENT_TYPE -from swift.common.swob import HTTPOk, HTTPNoContent, HTTPNotAcceptable -from swift.common.request_helpers import get_param +from swift.common.swob import HTTPOk, HTTPNoContent from swift.common.utils import json, normalize_timestamp @@ -43,25 +41,6 @@ class FakeAccountBroker(object): return {} -def account_listing_content_type(req): - """ - Figure out the content type of an account-listing response. - - Returns a 2-tuple: (content_type, error). Only one of them will be set; - the other will be None. - """ - query_format = get_param(req, 'format') - if query_format: - req.accept = FORMAT2CONTENT_TYPE.get(query_format.lower(), - FORMAT2CONTENT_TYPE['plain']) - content_type = req.accept.best_match( - ['text/plain', 'application/json', 'application/xml', 'text/xml']) - if not content_type: - return (None, HTTPNotAcceptable(request=req)) - else: - return (content_type, None) - - def account_listing_response(account, req, response_content_type, broker=None, limit='', marker='', end_marker='', prefix='', delimiter=''): diff --git a/swift/common/request_helpers.py b/swift/common/request_helpers.py index 5dd8b940fa..283a7be98a 100644 --- a/swift/common/request_helpers.py +++ b/swift/common/request_helpers.py @@ -20,8 +20,8 @@ Why not swift.common.utils, you ask? Because this way we can import things from swob in here without creating circular imports. """ - -from swift.common.swob import HTTPBadRequest +from swift.common.constraints import FORMAT2CONTENT_TYPE +from swift.common.swob import HTTPBadRequest, HTTPNotAcceptable def get_param(req, name, default=None): @@ -45,3 +45,25 @@ def get_param(req, name, default=None): request=req, content_type='text/plain', body='"%s" parameter not valid UTF-8' % name) return value + + +def get_listing_content_type(req): + """ + Determine the content type to use for an account or container listing + response. + + :param req: request object + :returns: content type as a string (e.g. text/plain, application/json) + :raises: HTTPNotAcceptable if the requested content type is not acceptable + :raises: HTTPBadRequest if the 'format' query param is provided and + not valid UTF-8 + """ + query_format = get_param(req, 'format') + if query_format: + req.accept = FORMAT2CONTENT_TYPE.get( + query_format.lower(), FORMAT2CONTENT_TYPE['plain']) + out_content_type = req.accept.best_match( + ['text/plain', 'application/json', 'application/xml', 'text/xml']) + if not out_content_type: + raise HTTPNotAcceptable(request=req) + return out_content_type diff --git a/swift/container/server.py b/swift/container/server.py index 0da4a0c4d3..c2f75d8fae 100644 --- a/swift/container/server.py +++ b/swift/container/server.py @@ -26,13 +26,13 @@ from eventlet import Timeout import swift.common.db from swift.common.db import ContainerBroker -from swift.common.request_helpers import get_param +from swift.common.request_helpers import get_param, get_listing_content_type from swift.common.utils import get_logger, hash_path, public, \ normalize_timestamp, storage_directory, validate_sync_to, \ config_true_value, validate_device_partition, json, timing_stats, \ replication, parse_content_type from swift.common.constraints import CONTAINER_LISTING_LIMIT, \ - check_mount, check_float, check_utf8, FORMAT2CONTENT_TYPE + check_mount, check_float, check_utf8 from swift.common.bufferedhttp import http_connect from swift.common.exceptions import ConnectionTimeout from swift.common.db_replicator import ReplicatorRpc @@ -40,7 +40,7 @@ from swift.common.http import HTTP_NOT_FOUND, is_success from swift.common.swob import HTTPAccepted, HTTPBadRequest, HTTPConflict, \ HTTPCreated, HTTPInternalServerError, HTTPNoContent, HTTPNotFound, \ HTTPPreconditionFailed, HTTPMethodNotAllowed, Request, Response, \ - HTTPInsufficientStorage, HTTPNotAcceptable, HTTPException, HeaderKeyDict + HTTPInsufficientStorage, HTTPException, HeaderKeyDict DATADIR = 'containers' @@ -300,14 +300,7 @@ class ContainerController(object): except ValueError, err: return HTTPBadRequest(body=str(err), content_type='text/plain', request=req) - query_format = get_param(req, 'format') - if query_format: - req.accept = FORMAT2CONTENT_TYPE.get( - query_format.lower(), FORMAT2CONTENT_TYPE['plain']) - out_content_type = req.accept.best_match( - ['text/plain', 'application/json', 'application/xml', 'text/xml']) - if not out_content_type: - return HTTPNotAcceptable(request=req) + out_content_type = get_listing_content_type(req) if self.mount_check and not check_mount(self.root, drive): return HTTPInsufficientStorage(drive=drive, request=req) broker = self._get_container_broker(drive, part, account, container, @@ -388,14 +381,7 @@ class ContainerController(object): return HTTPPreconditionFailed( request=req, body='Maximum limit is %d' % CONTAINER_LISTING_LIMIT) - query_format = get_param(req, 'format') - if query_format: - req.accept = FORMAT2CONTENT_TYPE.get(query_format.lower(), - FORMAT2CONTENT_TYPE['plain']) - out_content_type = req.accept.best_match( - ['text/plain', 'application/json', 'application/xml', 'text/xml']) - if not out_content_type: - return HTTPNotAcceptable(request=req) + out_content_type = get_listing_content_type(req) if self.mount_check and not check_mount(self.root, drive): return HTTPInsufficientStorage(drive=drive, request=req) broker = self._get_container_broker(drive, part, account, container, diff --git a/swift/proxy/controllers/account.py b/swift/proxy/controllers/account.py index 04af4d1cd9..d0cffddc4f 100644 --- a/swift/proxy/controllers/account.py +++ b/swift/proxy/controllers/account.py @@ -27,8 +27,8 @@ from gettext import gettext as _ from urllib import unquote -from swift.account.utils import account_listing_response, \ - account_listing_content_type +from swift.account.utils import account_listing_response +from swift.common.request_helpers import get_listing_content_type from swift.common.utils import public from swift.common.constraints import check_metadata, MAX_ACCOUNT_NAME_LENGTH from swift.common.http import HTTP_NOT_FOUND @@ -60,11 +60,8 @@ class AccountController(Controller): req, _('Account'), self.app.account_ring, partition, req.path_info.rstrip('/')) if resp.status_int == HTTP_NOT_FOUND and self.app.account_autocreate: - content_type, error = account_listing_content_type(req) - if error: - return error resp = account_listing_response(self.account_name, req, - content_type) + get_listing_content_type(req)) if not req.environ.get('swift_owner', False): for key in self.app.swift_owner_headers: if key in resp.headers: diff --git a/swift/proxy/server.py b/swift/proxy/server.py index 4601a72a9e..15814d5b9c 100644 --- a/swift/proxy/server.py +++ b/swift/proxy/server.py @@ -42,7 +42,7 @@ from swift.proxy.controllers import AccountController, ObjectController, \ ContainerController from swift.common.swob import HTTPBadRequest, HTTPForbidden, \ HTTPMethodNotAllowed, HTTPNotFound, HTTPPreconditionFailed, \ - HTTPServerError, Request + HTTPServerError, HTTPException, Request class Application(object): @@ -293,6 +293,8 @@ class Application(object): # method the client actually sent. req.environ['swift.orig_req_method'] = req.method return handler(req) + except HTTPException as error_response: + return error_response except (Exception, Timeout): self.logger.exception(_('ERROR Unhandled exception in request')) return HTTPServerError(request=req) diff --git a/test/unit/account/test_server.py b/test/unit/account/test_server.py index 8f2afb22f3..94a7094c99 100644 --- a/test/unit/account/test_server.py +++ b/test/unit/account/test_server.py @@ -129,7 +129,7 @@ class TestAccountController(unittest.TestCase): def test_HEAD_not_found(self): # Test the case in which account does not exist (can be recreated) req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'HEAD'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 404) self.assertTrue('X-Account-Status' not in resp.headers) @@ -156,7 +156,7 @@ class TestAccountController(unittest.TestCase): 'HTTP_X_TIMESTAMP': '0'}) self.controller.PUT(req) req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'HEAD'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 204) self.assertEquals(resp.headers['x-account-container-count'], '0') self.assertEquals(resp.headers['x-account-object-count'], '0') @@ -181,7 +181,7 @@ class TestAccountController(unittest.TestCase): 'X-Timestamp': normalize_timestamp(0)}) self.controller.PUT(req) req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'HEAD'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 204) self.assertEquals(resp.headers['x-account-container-count'], '2') self.assertEquals(resp.headers['x-account-object-count'], '0') @@ -202,7 +202,7 @@ class TestAccountController(unittest.TestCase): self.controller.PUT(req) req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'HEAD', 'HTTP_X_TIMESTAMP': '5'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 204) self.assertEquals(resp.headers['x-account-container-count'], '2') self.assertEquals(resp.headers['x-account-object-count'], '4') @@ -211,20 +211,20 @@ class TestAccountController(unittest.TestCase): def test_HEAD_invalid_partition(self): req = Request.blank('/sda1/./a', environ={'REQUEST_METHOD': 'HEAD', 'HTTP_X_TIMESTAMP': '1'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 400) def test_HEAD_invalid_content_type(self): req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'HEAD'}, headers={'Accept': 'application/plain'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 406) def test_HEAD_insufficient_storage(self): self.controller = AccountController({'devices': self.testdir}) req = Request.blank('/sda-null/p/a', environ={'REQUEST_METHOD': 'HEAD', 'HTTP_X_TIMESTAMP': '1'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 507) def test_HEAD_invalid_format(self): @@ -349,7 +349,7 @@ class TestAccountController(unittest.TestCase): resp = self.controller.POST(req) self.assertEquals(resp.status_int, 204) req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'HEAD'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 204) self.assertEquals(resp.headers.get('x-account-meta-test'), 'Value') # Update metadata header @@ -359,7 +359,7 @@ class TestAccountController(unittest.TestCase): resp = self.controller.POST(req) self.assertEquals(resp.status_int, 204) req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'HEAD'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 204) self.assertEquals(resp.headers.get('x-account-meta-test'), 'New Value') # Send old update to metadata header @@ -369,7 +369,7 @@ class TestAccountController(unittest.TestCase): resp = self.controller.POST(req) self.assertEquals(resp.status_int, 204) req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'HEAD'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 204) self.assertEquals(resp.headers.get('x-account-meta-test'), 'New Value') # Remove metadata header (by setting it to empty) @@ -379,7 +379,7 @@ class TestAccountController(unittest.TestCase): resp = self.controller.POST(req) self.assertEquals(resp.status_int, 204) req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'HEAD'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 204) self.assert_('x-account-meta-test' not in resp.headers) @@ -974,14 +974,14 @@ class TestAccountController(unittest.TestCase): self.controller.PUT(req) req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'GET'}) req.accept = 'application/xml*' - resp = self.controller.GET(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 406) def test_GET_delimiter_too_long(self): req = Request.blank('/sda1/p/a?delimiter=xx', environ={'REQUEST_METHOD': 'GET', 'HTTP_X_TIMESTAMP': '0'}) - resp = self.controller.GET(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 412) def test_GET_prefix_delimiter_plain(self): @@ -1306,22 +1306,22 @@ class TestAccountController(unittest.TestCase): env = {'REQUEST_METHOD': 'HEAD'} req = Request.blank('/sda1/p/a?format=xml', environ=env) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.content_type, 'application/xml') req = Request.blank('/sda1/p/a?format=json', environ=env) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.content_type, 'application/json') self.assertEquals(resp.charset, 'utf-8') req = Request.blank('/sda1/p/a', environ=env) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.content_type, 'text/plain') self.assertEquals(resp.charset, 'utf-8') req = Request.blank( '/sda1/p/a', headers={'Accept': 'application/json'}, environ=env) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.content_type, 'application/json') self.assertEquals(resp.charset, 'utf-8') diff --git a/test/unit/container/test_server.py b/test/unit/container/test_server.py index 6a3b8b1b10..75d1e74564 100644 --- a/test/unit/container/test_server.py +++ b/test/unit/container/test_server.py @@ -64,9 +64,9 @@ class TestContainerController(unittest.TestCase): # Ensure no acl by default req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT'}, headers={'X-Timestamp': '0'}) - self.controller.PUT(req) + req.get_response(self.controller) req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'}) - response = self.controller.HEAD(req) + response = req.get_response(self.controller) self.assert_(response.status.startswith('204')) self.assert_('x-container-read' not in response.headers) self.assert_('x-container-write' not in response.headers) @@ -74,9 +74,9 @@ class TestContainerController(unittest.TestCase): req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'POST'}, headers={'X-Timestamp': '1', 'X-Container-Read': '.r:*', 'X-Container-Write': 'account:user'}) - self.controller.POST(req) + resp = req.get_response(self.controller) req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'}) - response = self.controller.HEAD(req) + response = req.get_response(self.controller) self.assert_(response.status.startswith('204')) self.assertEquals(response.headers.get('x-container-read'), '.r:*') self.assertEquals(response.headers.get('x-container-write'), @@ -85,9 +85,9 @@ class TestContainerController(unittest.TestCase): req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'POST'}, headers={'X-Timestamp': '3', 'X-Container-Read': '', 'X-Container-Write': ''}) - self.controller.POST(req) + resp = req.get_response(self.controller) req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'}) - response = self.controller.HEAD(req) + response = req.get_response(self.controller) self.assert_(response.status.startswith('204')) self.assert_('x-container-read' not in response.headers) self.assert_('x-container-write' not in response.headers) @@ -95,9 +95,9 @@ class TestContainerController(unittest.TestCase): req = Request.blank('/sda1/p/a/c2', environ={'REQUEST_METHOD': 'PUT'}, headers={'X-Timestamp': '4', 'X-Container-Read': '.r:*', 'X-Container-Write': 'account:user'}) - self.controller.PUT(req) + resp = req.get_response(self.controller) req = Request.blank('/sda1/p/a/c2', environ={'REQUEST_METHOD': 'HEAD'}) - response = self.controller.HEAD(req) + response = req.get_response(self.controller) self.assert_(response.status.startswith('204')) self.assertEquals(response.headers.get('x-container-read'), '.r:*') self.assertEquals(response.headers.get('x-container-write'), @@ -106,28 +106,31 @@ class TestContainerController(unittest.TestCase): def test_HEAD(self): req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT', 'HTTP_X_TIMESTAMP': '0'}) - self.controller.PUT(req) - response = self.controller.HEAD(req) + req.get_response(self.controller) + req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD', + 'HTTP_X_TIMESTAMP': '0'}) + response = req.get_response(self.controller) self.assert_(response.status.startswith('204')) self.assertEquals(int(response.headers['x-container-bytes-used']), 0) self.assertEquals(int(response.headers['x-container-object-count']), 0) req2 = Request.blank('/sda1/p/a/c/o', environ={ + 'REQUEST_METHOD': 'PUT', 'HTTP_X_TIMESTAMP': '1', 'HTTP_X_SIZE': 42, 'HTTP_X_CONTENT_TYPE': 'text/plain', 'HTTP_X_ETAG': 'x'}) - self.controller.PUT(req2) - response = self.controller.HEAD(req) + req2.get_response(self.controller) + response = req.get_response(self.controller) self.assertEquals(int(response.headers['x-container-bytes-used']), 42) self.assertEquals(int(response.headers['x-container-object-count']), 1) def test_HEAD_not_found(self): req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 404) def test_HEAD_invalid_partition(self): req = Request.blank('/sda1/./a/c', environ={'REQUEST_METHOD': 'HEAD', 'HTTP_X_TIMESTAMP': '1'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 400) def test_HEAD_insufficient_storage(self): @@ -135,13 +138,13 @@ class TestContainerController(unittest.TestCase): {'devices': self.testdir}) req = Request.blank('/sda-null/p/a/c', environ={'REQUEST_METHOD': 'HEAD', 'HTTP_X_TIMESTAMP': '1'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 507) def test_HEAD_invalid_content_type(self): req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'}, headers={'Accept': 'application/plain'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 406) def test_HEAD_invalid_format(self): @@ -258,7 +261,7 @@ class TestContainerController(unittest.TestCase): resp = self.controller.POST(req) self.assertEquals(resp.status_int, 204) req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 204) self.assertEquals(resp.headers.get('x-container-meta-test'), 'Value') # Update metadata header @@ -268,7 +271,7 @@ class TestContainerController(unittest.TestCase): resp = self.controller.POST(req) self.assertEquals(resp.status_int, 204) req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 204) self.assertEquals(resp.headers.get('x-container-meta-test'), 'New Value') @@ -279,7 +282,7 @@ class TestContainerController(unittest.TestCase): resp = self.controller.POST(req) self.assertEquals(resp.status_int, 204) req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 204) self.assertEquals(resp.headers.get('x-container-meta-test'), 'New Value') @@ -290,14 +293,14 @@ class TestContainerController(unittest.TestCase): resp = self.controller.POST(req) self.assertEquals(resp.status_int, 204) req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 204) self.assert_('x-container-meta-test' not in resp.headers) def test_POST_invalid_partition(self): req = Request.blank('/sda1/./a/c', environ={'REQUEST_METHOD': 'POST', 'HTTP_X_TIMESTAMP': '1'}) - resp = self.controller.POST(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 400) def test_POST_timestamp_not_float(self): @@ -306,7 +309,7 @@ class TestContainerController(unittest.TestCase): self.controller.PUT(req) req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'POST'}, headers={'X-Timestamp': 'not-float'}) - resp = self.controller.POST(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 400) def test_POST_insufficient_storage(self): @@ -314,7 +317,7 @@ class TestContainerController(unittest.TestCase): {'devices': self.testdir}) req = Request.blank('/sda-null/p/a/c', environ={'REQUEST_METHOD': 'POST', 'HTTP_X_TIMESTAMP': '1'}) - resp = self.controller.POST(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 507) def test_POST_invalid_container_sync_to(self): @@ -323,22 +326,22 @@ class TestContainerController(unittest.TestCase): req = Request.blank('/sda-null/p/a/c', environ={'REQUEST_METHOD': 'POST', 'HTTP_X_TIMESTAMP': '1'}, headers={'x-container-sync-to': '192.168.0.1'}) - resp = self.controller.POST(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 400) def test_POST_after_DELETE_not_found(self): req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT'}, headers={'X-Timestamp': '1'}) - self.controller.PUT(req) + resp = req.get_response(self.controller) req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'DELETE'}, headers={'X-Timestamp': '2'}) - self.controller.DELETE(req) + resp = req.get_response(self.controller) req = Request.blank('/sda1/p/a/c/', environ={'REQUEST_METHOD': 'POST'}, headers={'X-Timestamp': '3'}) - resp = self.controller.POST(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 404) def test_DELETE_obj_not_found(self): @@ -392,7 +395,7 @@ class TestContainerController(unittest.TestCase): with save_globals(): new_connect = fake_http_connect(200, count=123) swift.container.server.http_connect = new_connect - resp = self.controller.PUT(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 201) def test_PUT_account_update(self): @@ -758,7 +761,9 @@ class TestContainerController(unittest.TestCase): self.assertEquals(simplejson.loads(resp.body), json_body) self.assertEquals(resp.charset, 'utf-8') - resp = self.controller.HEAD(req) + req = Request.blank('/sda1/p/a/jsonc?format=json', + environ={'REQUEST_METHOD': 'HEAD'}) + resp = req.get_response(self.controller) self.assertEquals(resp.content_type, 'application/json') for accept in ('application/json', 'application/json;q=1.0,*/*;q=0.9', @@ -766,13 +771,16 @@ class TestContainerController(unittest.TestCase): req = Request.blank('/sda1/p/a/jsonc', environ={'REQUEST_METHOD': 'GET'}) req.accept = accept - resp = self.controller.GET(req) + resp = req.get_response(self.controller) self.assertEquals(simplejson.loads(resp.body), json_body, 'Invalid body for Accept: %s' % accept) self.assertEquals(resp.content_type, 'application/json', 'Invalid content_type for Accept: %s' % accept) - resp = self.controller.HEAD(req) + req = Request.blank('/sda1/p/a/jsonc', + environ={'REQUEST_METHOD': 'HEAD'}) + req.accept = accept + resp = req.get_response(self.controller) self.assertEquals(resp.content_type, 'application/json', 'Invalid content_type for Accept: %s' % accept) @@ -804,7 +812,9 @@ class TestContainerController(unittest.TestCase): self.assertEquals(resp.body, plain_body) self.assertEquals(resp.charset, 'utf-8') - resp = self.controller.HEAD(req) + req = Request.blank('/sda1/p/a/plainc', + environ={'REQUEST_METHOD': 'HEAD'}) + resp = req.get_response(self.controller) self.assertEquals(resp.content_type, 'text/plain') for accept in ('', 'text/plain', 'application/xml;q=0.8,*/*;q=0.9', @@ -819,7 +829,10 @@ class TestContainerController(unittest.TestCase): self.assertEquals(resp.content_type, 'text/plain', 'Invalid content_type for Accept: %s' % accept) - resp = self.controller.HEAD(req) + req = Request.blank('/sda1/p/a/plainc', + environ={'REQUEST_METHOD': 'GET'}) + req.accept = accept + resp = req.get_response(self.controller) self.assertEquals(resp.content_type, 'text/plain', 'Invalid content_type for Accept: %s' % accept) @@ -915,7 +928,9 @@ class TestContainerController(unittest.TestCase): self.assertEquals(resp.body, xml_body) self.assertEquals(resp.charset, 'utf-8') - resp = self.controller.HEAD(req) + req = Request.blank('/sda1/p/a/xmlc?format=xml', + environ={'REQUEST_METHOD': 'HEAD'}) + resp = req.get_response(self.controller) self.assertEquals(resp.content_type, 'application/xml') for xml_accept in ('application/xml', 'application/xml;q=1.0,*/*;q=0.9', @@ -929,7 +944,10 @@ class TestContainerController(unittest.TestCase): self.assertEquals(resp.content_type, 'application/xml', 'Invalid content_type for Accept: %s' % xml_accept) - resp = self.controller.HEAD(req) + req = Request.blank('/sda1/p/a/xmlc', + environ={'REQUEST_METHOD': 'HEAD'}) + req.accept = xml_accept + resp = req.get_response(self.controller) self.assertEquals(resp.content_type, 'application/xml', 'Invalid content_type for Accept: %s' % xml_accept) @@ -944,13 +962,13 @@ class TestContainerController(unittest.TestCase): # make a container req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT', 'HTTP_X_TIMESTAMP': '0'}) - resp = self.controller.PUT(req) + resp = req.get_response(self.controller) # fill the container for i in range(3): req = Request.blank('/sda1/p/a/c/%s' % i, environ={'REQUEST_METHOD': 'PUT', 'HTTP_X_TIMESTAMP': '1', 'HTTP_X_CONTENT_TYPE': 'text/plain', 'HTTP_X_ETAG': 'x', 'HTTP_X_SIZE': 0}) - resp = self.controller.PUT(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 201) # test limit with marker req = Request.blank('/sda1/p/a/c?limit=2&marker=1', environ={'REQUEST_METHOD': 'GET'}) @@ -962,7 +980,7 @@ class TestContainerController(unittest.TestCase): snowman = u'\u2603' req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT', 'HTTP_X_TIMESTAMP': '0'}) - resp = self.controller.PUT(req) + resp = req.get_response(self.controller) for i, ctype in enumerate((snowman.encode('utf-8'), 'text/plain; charset="utf-8"')): req = Request.blank('/sda1/p/a/c/%s' % i, environ={ @@ -986,17 +1004,17 @@ class TestContainerController(unittest.TestCase): 'X-Object-Count': '0', 'X-Bytes-Used': '0', 'X-Timestamp': normalize_timestamp(0)}) - self.controller.PUT(req) + req.get_response(self.controller) req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'GET'}) req.accept = 'application/xml*' - resp = self.controller.GET(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 406) def test_GET_limit(self): # make a container req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT', 'HTTP_X_TIMESTAMP': '0'}) - resp = self.controller.PUT(req) + resp = req.get_response(self.controller) # fill the container for i in range(3): req = Request.blank('/sda1/p/a/c/%s' % i, @@ -1006,11 +1024,11 @@ class TestContainerController(unittest.TestCase): 'HTTP_X_CONTENT_TYPE': 'text/plain', 'HTTP_X_ETAG': 'x', 'HTTP_X_SIZE': 0}) - resp = self.controller.PUT(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 201) # test limit req = Request.blank('/sda1/p/a/c?limit=2', environ={'REQUEST_METHOD': 'GET'}) - resp = self.controller.GET(req) + resp = req.get_response(self.controller) result = resp.body.split() self.assertEquals(result, ['0', '1']) @@ -1026,17 +1044,17 @@ class TestContainerController(unittest.TestCase): 'HTTP_X_CONTENT_TYPE': 'text/plain', 'HTTP_X_ETAG': 'x', 'HTTP_X_SIZE': 0}) - resp = self.controller.PUT(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 201) req = Request.blank('/sda1/p/a/c?prefix=a', environ={'REQUEST_METHOD': 'GET'}) - resp = self.controller.GET(req) + resp = req.get_response(self.controller) self.assertEquals(resp.body.split(), ['a1', 'a2', 'a3']) def test_GET_delimiter_too_long(self): req = Request.blank('/sda1/p/a/c?delimiter=xx', environ={'REQUEST_METHOD': 'GET', 'HTTP_X_TIMESTAMP': '0'}) - resp = self.controller.GET(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 412) def test_GET_delimiter(self): @@ -1049,11 +1067,11 @@ class TestContainerController(unittest.TestCase): 'REQUEST_METHOD': 'PUT', 'HTTP_X_TIMESTAMP': '1', 'HTTP_X_CONTENT_TYPE': 'text/plain', 'HTTP_X_ETAG': 'x', 'HTTP_X_SIZE': 0}) - resp = self.controller.PUT(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 201) req = Request.blank('/sda1/p/a/c?prefix=US-&delimiter=-&format=json', environ={'REQUEST_METHOD': 'GET'}) - resp = self.controller.GET(req) + resp = req.get_response(self.controller) self.assertEquals(simplejson.loads(resp.body), [{"subdir": "US-OK-"}, {"subdir": "US-TX-"}, @@ -1324,29 +1342,29 @@ class TestContainerController(unittest.TestCase): env = {'REQUEST_METHOD': 'HEAD'} req = Request.blank('/sda1/p/a/o?format=xml', environ=env) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.content_type, 'application/xml') self.assertEquals(resp.charset, 'utf-8') req = Request.blank('/sda1/p/a/o?format=json', environ=env) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.content_type, 'application/json') self.assertEquals(resp.charset, 'utf-8') req = Request.blank('/sda1/p/a/o', environ=env) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.content_type, 'text/plain') self.assertEquals(resp.charset, 'utf-8') req = Request.blank( '/sda1/p/a/o', headers={'Accept': 'application/json'}, environ=env) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.content_type, 'application/json') self.assertEquals(resp.charset, 'utf-8') req = Request.blank( '/sda1/p/a/o', headers={'Accept': 'application/xml'}, environ=env) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.content_type, 'application/xml') self.assertEquals(resp.charset, 'utf-8') diff --git a/test/unit/proxy/test_server.py b/test/unit/proxy/test_server.py index 8cce8d0ed8..034228f400 100644 --- a/test/unit/proxy/test_server.py +++ b/test/unit/proxy/test_server.py @@ -5772,7 +5772,6 @@ class TestAccountController(unittest.TestCase): req.content_length = 0 resp = controller.OPTIONS(req) self.assertEquals(200, resp.status_int) - print resp.headers['Allow'] for verb in 'OPTIONS GET POST PUT DELETE HEAD'.split(): self.assertTrue( verb in resp.headers['Allow']) @@ -6140,20 +6139,20 @@ class TestAccountControllerFakeGetResponse(unittest.TestCase): have to match the responses for empty accounts that really exist. """ def setUp(self): - self.app = proxy_server.Application(None, FakeMemcache(), + conf = {'account_autocreate': 'yes'} + self.app = proxy_server.Application(conf, FakeMemcache(), account_ring=FakeRing(), container_ring=FakeRing(), object_ring=FakeRing) self.app.memcache = FakeMemcacheReturnsNone() - self.controller = proxy_server.AccountController(self.app, 'acc') - self.controller.app.account_autocreate = True def test_GET_autocreate_accept_json(self): with save_globals(): - set_http_connect(404) # however many backends we ask, they all 404 - req = Request.blank('/a', headers={'Accept': 'application/json'}) - - resp = self.controller.GET(req) + set_http_connect(*([404] * 100)) # nonexistent: all backends 404 + req = Request.blank('/v1/a', headers={'Accept': 'application/json'}, + environ={'REQUEST_METHOD': 'GET', + 'PATH_INFO': '/v1/a'}) + resp = req.get_response(self.app) self.assertEqual(200, resp.status_int) self.assertEqual('application/json; charset=utf-8', resp.headers['Content-Type']) @@ -6161,10 +6160,12 @@ class TestAccountControllerFakeGetResponse(unittest.TestCase): def test_GET_autocreate_format_json(self): with save_globals(): - set_http_connect(404) # however many backends we ask, they all 404 - req = Request.blank('/a?format=json') - - resp = self.controller.GET(req) + set_http_connect(*([404] * 100)) # nonexistent: all backends 404 + req = Request.blank('/v1/a?format=json', + environ={'REQUEST_METHOD': 'GET', + 'PATH_INFO': '/v1/a', + 'QUERY_STRING': 'format=json'}) + resp = req.get_response(self.app) self.assertEqual(200, resp.status_int) self.assertEqual('application/json; charset=utf-8', resp.headers['Content-Type']) @@ -6172,30 +6173,54 @@ class TestAccountControllerFakeGetResponse(unittest.TestCase): def test_GET_autocreate_accept_xml(self): with save_globals(): - set_http_connect(404) # however many backends we ask, they all 404 - req = Request.blank('/a', headers={"Accept": "text/xml"}) + set_http_connect(*([404] * 100)) # nonexistent: all backends 404 + req = Request.blank('/v1/a', headers={"Accept": "text/xml"}, + environ={'REQUEST_METHOD': 'GET', + 'PATH_INFO': '/v1/a'}) - resp = self.controller.GET(req) + resp = req.get_response(self.app) self.assertEqual(200, resp.status_int) + self.assertEqual('text/xml; charset=utf-8', resp.headers['Content-Type']) empty_xml_listing = ('\n' - '\n') + '\n') self.assertEqual(empty_xml_listing, resp.body) def test_GET_autocreate_format_xml(self): with save_globals(): - set_http_connect(404) # however many backends we ask, they all 404 - req = Request.blank('/a?format=xml') - - resp = self.controller.GET(req) + set_http_connect(*([404] * 100)) # nonexistent: all backends 404 + req = Request.blank('/v1/a?format=xml', + environ={'REQUEST_METHOD': 'GET', + 'PATH_INFO': '/v1/a', + 'QUERY_STRING': 'format=xml'}) + resp = req.get_response(self.app) self.assertEqual(200, resp.status_int) self.assertEqual('application/xml; charset=utf-8', resp.headers['Content-Type']) empty_xml_listing = ('\n' - '\n') + '\n') self.assertEqual(empty_xml_listing, resp.body) + def test_GET_autocreate_accept_unknown(self): + with save_globals(): + set_http_connect(*([404] * 100)) # nonexistent: all backends 404 + req = Request.blank('/v1/a', headers={"Accept": "mystery/meat"}, + environ={'REQUEST_METHOD': 'GET', + 'PATH_INFO': '/v1/a'}) + resp = req.get_response(self.app) + self.assertEqual(406, resp.status_int) + + def test_GET_autocreate_format_invalid_utf8(self): + with save_globals(): + set_http_connect(*([404] * 100)) # nonexistent: all backends 404 + req = Request.blank('/v1/a?format=\xff\xfe', + environ={'REQUEST_METHOD': 'GET', + 'PATH_INFO': '/v1/a', + 'QUERY_STRING': 'format=\xff\xfe'}) + resp = req.get_response(self.app) + self.assertEqual(400, resp.status_int) + class FakeObjectController(object):