From df7fc9658b350cf38d8ecf4e49043fbe44e1d2d3 Mon Sep 17 00:00:00 2001 From: Samuel Merritt Date: Wed, 24 Jul 2013 12:55:25 -0700 Subject: [PATCH] Catch swob responses that are raised. This lets us get rid of some really repetitive exception conversion code, like everybody that called common.utils.get_param() had to catch a UnicodeDecodeError and turn that into returning HTTPBadRequest. Now get_param() just raises HTTPBadRequest directly, and the __call__ methods in the account/container/object servers catch and return it. All that "except UnicodeDecodeError" stuff goes away. Refactored the path splitting and device validation in the object server too. There are other things that can benefit from this as well, but this patch is big enough. Change-Id: I2be96ef757d04bfd6af180cd9c92393c841db21f --- swift/account/server.py | 47 +++++++------- swift/account/utils.py | 12 ++-- swift/common/request_helpers.py | 47 ++++++++++++++ swift/common/utils.py | 16 ----- swift/container/server.py | 51 +++++++--------- swift/obj/server.py | 66 +++++++++----------- test/unit/account/test_server.py | 10 +-- test/unit/container/test_server.py | 4 +- test/unit/obj/test_server.py | 98 +++++++++++++++--------------- 9 files changed, 179 insertions(+), 172 deletions(-) create mode 100644 swift/common/request_helpers.py diff --git a/swift/account/server.py b/swift/account/server.py index 848c91780b..e53929fd85 100644 --- a/swift/account/server.py +++ b/swift/account/server.py @@ -25,7 +25,8 @@ import swift.common.db from swift.account.utils import account_listing_response, \ account_listing_content_type from swift.common.db import AccountBroker, DatabaseConnectionError -from swift.common.utils import get_logger, get_param, hash_path, public, \ +from swift.common.request_helpers import get_param +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, \ @@ -35,7 +36,7 @@ from swift.common.swob import HTTPAccepted, HTTPBadRequest, \ HTTPCreated, HTTPForbidden, HTTPInternalServerError, \ HTTPMethodNotAllowed, HTTPNoContent, HTTPNotFound, \ HTTPPreconditionFailed, HTTPConflict, Request, \ - HTTPInsufficientStorage, HTTPNotAcceptable + HTTPInsufficientStorage, HTTPNotAcceptable, HTTPException DATADIR = 'accounts' @@ -176,11 +177,7 @@ class AccountController(object): except ValueError, err: return HTTPBadRequest(body=str(err), content_type='text/plain', request=req) - try: - query_format = get_param(req, 'format') - except UnicodeDecodeError: - return HTTPBadRequest(body='parameters not utf8', - 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']) @@ -218,25 +215,21 @@ class AccountController(object): except ValueError, err: return HTTPBadRequest(body=str(err), content_type='text/plain', request=req) - try: - prefix = get_param(req, 'prefix') - delimiter = get_param(req, 'delimiter') - if delimiter and (len(delimiter) > 1 or ord(delimiter) > 254): - # delimiters can be made more flexible later - return HTTPPreconditionFailed(body='Bad delimiter') - limit = ACCOUNT_LISTING_LIMIT - given_limit = get_param(req, 'limit') - if given_limit and given_limit.isdigit(): - limit = int(given_limit) - if limit > ACCOUNT_LISTING_LIMIT: - return HTTPPreconditionFailed(request=req, - body='Maximum limit is %d' % - ACCOUNT_LISTING_LIMIT) - marker = get_param(req, 'marker', '') - end_marker = get_param(req, 'end_marker') - except UnicodeDecodeError, err: - return HTTPBadRequest(body='parameters not utf8', - content_type='text/plain', request=req) + prefix = get_param(req, 'prefix') + delimiter = get_param(req, 'delimiter') + if delimiter and (len(delimiter) > 1 or ord(delimiter) > 254): + # delimiters can be made more flexible later + return HTTPPreconditionFailed(body='Bad delimiter') + limit = ACCOUNT_LISTING_LIMIT + given_limit = get_param(req, 'limit') + if given_limit and given_limit.isdigit(): + limit = int(given_limit) + if limit > ACCOUNT_LISTING_LIMIT: + return HTTPPreconditionFailed(request=req, + body='Maximum limit is %d' % + 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 @@ -326,6 +319,8 @@ class AccountController(object): res = HTTPMethodNotAllowed() else: res = method(req) + except HTTPException as error_response: + res = error_response except (Exception, Timeout): self.logger.exception(_('ERROR __call__ error with %(method)s' ' %(path)s '), diff --git a/swift/account/utils.py b/swift/account/utils.py index 6b18850f78..e4c3b053b8 100644 --- a/swift/account/utils.py +++ b/swift/account/utils.py @@ -17,9 +17,9 @@ import time from xml.sax import saxutils from swift.common.constraints import FORMAT2CONTENT_TYPE -from swift.common.swob import HTTPOk, HTTPNoContent, HTTPNotAcceptable, \ - HTTPBadRequest -from swift.common.utils import get_param, json, normalize_timestamp +from swift.common.swob import HTTPOk, HTTPNoContent, HTTPNotAcceptable +from swift.common.request_helpers import get_param +from swift.common.utils import json, normalize_timestamp class FakeAccountBroker(object): @@ -50,11 +50,7 @@ def account_listing_content_type(req): Returns a 2-tuple: (content_type, error). Only one of them will be set; the other will be None. """ - try: - query_format = get_param(req, 'format') - except UnicodeDecodeError: - return (None, HTTPBadRequest(body='parameters not utf8', - 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']) diff --git a/swift/common/request_helpers.py b/swift/common/request_helpers.py new file mode 100644 index 0000000000..5dd8b940fa --- /dev/null +++ b/swift/common/request_helpers.py @@ -0,0 +1,47 @@ +# Copyright (c) 2010-2013 OpenStack, LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Miscellaneous utility functions for use in generating responses. + +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 + + +def get_param(req, name, default=None): + """ + Get parameters from an HTTP request ensuring proper handling UTF-8 + encoding. + + :param req: request object + :param name: parameter name + :param default: result to return if the parameter is not found + :returns: HTTP request parameter value + (as UTF-8 encoded str, not unicode object) + :raises: HTTPBadRequest if param not valid UTF-8 byte sequence + """ + value = req.params.get(name, default) + if value and not isinstance(value, unicode): + try: + value.decode('utf8') # Ensure UTF8ness + except UnicodeDecodeError: + raise HTTPBadRequest( + request=req, content_type='text/plain', + body='"%s" parameter not valid UTF-8' % name) + return value diff --git a/swift/common/utils.py b/swift/common/utils.py index 5970165477..78f4fad8df 100644 --- a/swift/common/utils.py +++ b/swift/common/utils.py @@ -170,22 +170,6 @@ def load_libc_function(func_name, log_error=True): return noop_libc_function -def get_param(req, name, default=None): - """ - Get parameters from an HTTP request ensuring proper handling UTF-8 - encoding. - - :param req: request object - :param name: parameter name - :param default: result to return if the parameter is not found - :returns: HTTP request parameter value - """ - value = req.params.get(name, default) - if value and not isinstance(value, unicode): - value.decode('utf8') # Ensure UTF8ness - return value - - def generate_trans_id(trans_id_suffix): return 'tx%s-%010x%s' % ( uuid.uuid4().hex[:21], time.time(), trans_id_suffix) diff --git a/swift/container/server.py b/swift/container/server.py index 660ef947de..51fef685a1 100644 --- a/swift/container/server.py +++ b/swift/container/server.py @@ -25,7 +25,8 @@ from eventlet import Timeout import swift.common.db from swift.common.db import ContainerBroker -from swift.common.utils import get_logger, get_param, hash_path, public, \ +from swift.common.request_helpers import get_param +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 @@ -38,7 +39,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, HeaderKeyDict + HTTPInsufficientStorage, HTTPNotAcceptable, HTTPException, HeaderKeyDict DATADIR = 'containers' @@ -298,11 +299,7 @@ class ContainerController(object): except ValueError, err: return HTTPBadRequest(body=str(err), content_type='text/plain', request=req) - try: - query_format = get_param(req, 'format') - except UnicodeDecodeError: - return HTTPBadRequest(body='parameters not utf8', - 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']) @@ -362,27 +359,23 @@ class ContainerController(object): except ValueError, err: return HTTPBadRequest(body=str(err), content_type='text/plain', request=req) - try: - path = get_param(req, 'path') - prefix = get_param(req, 'prefix') - delimiter = get_param(req, 'delimiter') - if delimiter and (len(delimiter) > 1 or ord(delimiter) > 254): - # delimiters can be made more flexible later - return HTTPPreconditionFailed(body='Bad delimiter') - marker = get_param(req, 'marker', '') - end_marker = get_param(req, 'end_marker') - limit = CONTAINER_LISTING_LIMIT - given_limit = get_param(req, 'limit') - if given_limit and given_limit.isdigit(): - limit = int(given_limit) - if limit > CONTAINER_LISTING_LIMIT: - return HTTPPreconditionFailed( - request=req, - body='Maximum limit is %d' % CONTAINER_LISTING_LIMIT) - query_format = get_param(req, 'format') - except UnicodeDecodeError, err: - return HTTPBadRequest(body='parameters not utf8', - content_type='text/plain', request=req) + path = get_param(req, 'path') + prefix = get_param(req, 'prefix') + delimiter = get_param(req, 'delimiter') + if delimiter and (len(delimiter) > 1 or ord(delimiter) > 254): + # delimiters can be made more flexible later + return HTTPPreconditionFailed(body='Bad delimiter') + marker = get_param(req, 'marker', '') + end_marker = get_param(req, 'end_marker') + limit = CONTAINER_LISTING_LIMIT + given_limit = get_param(req, 'limit') + if given_limit and given_limit.isdigit(): + limit = int(given_limit) + if limit > CONTAINER_LISTING_LIMIT: + 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']) @@ -548,6 +541,8 @@ class ContainerController(object): res = HTTPMethodNotAllowed() else: res = method(req) + except HTTPException as error_response: + res = error_response except (Exception, Timeout): self.logger.exception(_( 'ERROR __call__ error with %(method)s %(path)s '), diff --git a/swift/obj/server.py b/swift/obj/server.py index 47a79abc47..37511bc090 100644 --- a/swift/obj/server.py +++ b/swift/obj/server.py @@ -43,7 +43,7 @@ from swift.common.swob import HTTPAccepted, HTTPBadRequest, HTTPCreated, \ HTTPInternalServerError, HTTPNoContent, HTTPNotFound, HTTPNotModified, \ HTTPPreconditionFailed, HTTPRequestTimeout, HTTPUnprocessableEntity, \ HTTPClientDisconnect, HTTPMethodNotAllowed, Request, Response, UTC, \ - HTTPInsufficientStorage, HTTPForbidden, HeaderKeyDict + HTTPInsufficientStorage, HTTPForbidden, HTTPException, HeaderKeyDict from swift.obj.diskfile import DiskFile @@ -276,17 +276,27 @@ class ObjectController(object): '%s-%s/%s/%s' % (delete_at, account, container, obj), host, partition, contdevice, headers_out, objdevice) + def _parse_path(self, request, minsegs=5, maxsegs=5): + """ + Utility function to split and validate the request path. + + :returns: result of split_path if everything's okay + :raises: HTTPBadRequest if something's not okay + """ + try: + segs = split_path(unquote(request.path), minsegs, maxsegs, True) + validate_device_partition(segs[0], segs[1]) + return segs + except ValueError as err: + raise HTTPBadRequest(body=str(err), request=request, + content_type='text/plain') + @public @timing_stats() def POST(self, request): """Handle HTTP POST requests for the Swift Object Server.""" - try: - device, partition, account, container, obj = \ - split_path(unquote(request.path), 5, 5, True) - validate_device_partition(device, partition) - except ValueError, err: - return HTTPBadRequest(body=str(err), request=request, - content_type='text/plain') + device, partition, account, container, obj = self._parse_path(request) + if 'x-timestamp' not in request.headers or \ not check_float(request.headers['x-timestamp']): return HTTPBadRequest(body='Missing timestamp', request=request, @@ -329,13 +339,8 @@ class ObjectController(object): @timing_stats() def PUT(self, request): """Handle HTTP PUT requests for the Swift Object Server.""" - try: - device, partition, account, container, obj = \ - split_path(unquote(request.path), 5, 5, True) - validate_device_partition(device, partition) - except ValueError, err: - return HTTPBadRequest(body=str(err), request=request, - content_type='text/plain') + device, partition, account, container, obj = self._parse_path(request) + if 'x-timestamp' not in request.headers or \ not check_float(request.headers['x-timestamp']): return HTTPBadRequest(body='Missing timestamp', request=request, @@ -428,13 +433,8 @@ class ObjectController(object): @timing_stats() def GET(self, request): """Handle HTTP GET requests for the Swift Object Server.""" - try: - device, partition, account, container, obj = \ - split_path(unquote(request.path), 5, 5, True) - validate_device_partition(device, partition) - except ValueError, err: - return HTTPBadRequest(body=str(err), request=request, - content_type='text/plain') + device, partition, account, container, obj = self._parse_path(request) + try: disk_file = self._diskfile(device, partition, account, container, obj, keep_data_fp=True, iter_hook=sleep) @@ -507,15 +507,8 @@ class ObjectController(object): @timing_stats(sample_rate=0.8) def HEAD(self, request): """Handle HTTP HEAD requests for the Swift Object Server.""" - try: - device, partition, account, container, obj = \ - split_path(unquote(request.path), 5, 5, True) - validate_device_partition(device, partition) - except ValueError, err: - resp = HTTPBadRequest(request=request) - resp.content_type = 'text/plain' - resp.body = str(err) - return resp + device, partition, account, container, obj = self._parse_path(request) + try: disk_file = self._diskfile(device, partition, account, container, obj) @@ -601,13 +594,8 @@ class ObjectController(object): Handle REPLICATE requests for the Swift Object Server. This is used by the object replicator to get hashes for directories. """ - try: - device, partition, suffix = split_path( - unquote(request.path), 2, 3, True) - validate_device_partition(device, partition) - except ValueError, e: - return HTTPBadRequest(body=str(e), request=request, - content_type='text/plain') + device, partition, suffix = self._parse_path(request, 2, 3) + if self.mount_check and not check_mount(self.devices, device): return HTTPInsufficientStorage(drive=device, request=request) path = os.path.join(self.devices, device, DATADIR, partition) @@ -642,6 +630,8 @@ class ObjectController(object): res = method(req) except DiskFileCollision: res = HTTPForbidden(request=req) + except HTTPException as error_response: + res = error_response except (Exception, Timeout): self.logger.exception(_( 'ERROR __call__ error with %(method)s' diff --git a/test/unit/account/test_server.py b/test/unit/account/test_server.py index 516cbce023..8f2afb22f3 100644 --- a/test/unit/account/test_server.py +++ b/test/unit/account/test_server.py @@ -145,9 +145,9 @@ class TestAccountController(unittest.TestCase): self.controller.PUT(req) req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'DELETE', 'HTTP_X_TIMESTAMP': '1'}) - resp = self.controller.DELETE(req) + resp = req.get_response(self.controller) 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.assertEquals(resp.headers['X-Account-Status'], 'Deleted') @@ -231,7 +231,7 @@ class TestAccountController(unittest.TestCase): format = '%D1%BD%8A9' # invalid UTF-8; should be %E1%BD%8A9 (E -> D) req = Request.blank('/sda1/p/a?format=' + format, environ={'REQUEST_METHOD': 'HEAD'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 400) def test_PUT_not_found(self): @@ -1251,7 +1251,7 @@ class TestAccountController(unittest.TestCase): for format in ('xml', 'json'): req = Request.blank('/sda1/p/a?format=%s' % format, environ={'REQUEST_METHOD': 'GET'}) - resp = self.controller.GET(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 200) def test_params_utf8(self): @@ -1260,7 +1260,7 @@ class TestAccountController(unittest.TestCase): 'format'): req = Request.blank('/sda1/p/a?%s=\xce' % param, environ={'REQUEST_METHOD': 'GET'}) - resp = self.controller.GET(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 400, "%d on param %s" % (resp.status_int, param)) # Good UTF8 sequence for delimiter, too long (1 byte delimiters only) diff --git a/test/unit/container/test_server.py b/test/unit/container/test_server.py index ab88d8cac5..e70c70e246 100644 --- a/test/unit/container/test_server.py +++ b/test/unit/container/test_server.py @@ -147,7 +147,7 @@ class TestContainerController(unittest.TestCase): format = '%D1%BD%8A9' # invalid UTF-8; should be %E1%BD%8A9 (E -> D) req = Request.blank('/sda1/p/a/c?format=' + format, environ={'REQUEST_METHOD': 'HEAD'}) - resp = self.controller.HEAD(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 400) def test_PUT(self): @@ -1245,7 +1245,7 @@ class TestContainerController(unittest.TestCase): 'end_marker', 'format'): req = Request.blank('/sda1/p/a/c?%s=\xce' % param, environ={'REQUEST_METHOD': 'GET'}) - resp = self.controller.GET(req) + resp = req.get_response(self.controller) self.assertEquals(resp.status_int, 400, "%d on param %s" % (resp.status_int, param)) # Good UTF8 sequence for delimiter, too long (1 byte delimiters only) diff --git a/test/unit/obj/test_server.py b/test/unit/obj/test_server.py index 77985de46b..75871e6059 100755 --- a/test/unit/obj/test_server.py +++ b/test/unit/obj/test_server.py @@ -231,7 +231,7 @@ class TestObjectController(unittest.TestCase): 'X-Object-Meta-1': 'One', 'X-Object-Meta-2': 'Two', 'Content-Type': 'text/plain'}) - resp = self.object_controller.POST(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 404) def test_POST_invalid_path(self): @@ -241,7 +241,7 @@ class TestObjectController(unittest.TestCase): 'X-Object-Meta-1': 'One', 'X-Object-Meta-2': 'Two', 'Content-Type': 'text/plain'}) - resp = self.object_controller.POST(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 400) def test_POST_container_connection(self): @@ -341,13 +341,13 @@ class TestObjectController(unittest.TestCase): def test_PUT_invalid_path(self): req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT'}) - resp = self.object_controller.PUT(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 400) def test_PUT_no_timestamp(self): req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'PUT', 'CONTENT_LENGTH': '0'}) - resp = self.object_controller.PUT(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 400) def test_PUT_no_content_type(self): @@ -355,7 +355,7 @@ class TestObjectController(unittest.TestCase): headers={'X-Timestamp': normalize_timestamp(time()), 'Content-Length': '6'}) req.body = 'VERIFY' - resp = self.object_controller.PUT(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 400) def test_PUT_invalid_content_type(self): @@ -364,7 +364,7 @@ class TestObjectController(unittest.TestCase): 'Content-Length': '6', 'Content-Type': '\xff\xff'}) req.body = 'VERIFY' - resp = self.object_controller.PUT(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 400) self.assert_('Content-Type' in resp.body) @@ -374,7 +374,7 @@ class TestObjectController(unittest.TestCase): 'Content-Type': 'application/octet-stream'}) req.body = 'VERIFY' del req.headers['Content-Length'] - resp = self.object_controller.PUT(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 411) def test_PUT_zero_content_length(self): @@ -383,7 +383,7 @@ class TestObjectController(unittest.TestCase): 'Content-Type': 'application/octet-stream'}) req.body = '' self.assertEquals(req.headers['Content-Length'], '0') - resp = self.object_controller.PUT(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 201) def test_PUT_common(self): @@ -393,7 +393,7 @@ class TestObjectController(unittest.TestCase): 'Content-Length': '6', 'Content-Type': 'application/octet-stream'}) req.body = 'VERIFY' - resp = self.object_controller.PUT(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 201) objfile = os.path.join(self.testdir, 'sda1', storage_directory(object_server.DATADIR, 'p', @@ -414,7 +414,7 @@ class TestObjectController(unittest.TestCase): 'Content-Length': '6', 'Content-Type': 'application/octet-stream'}) req.body = 'VERIFY' - resp = self.object_controller.PUT(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 201) sleep(.00001) timestamp = normalize_timestamp(time()) @@ -423,7 +423,7 @@ class TestObjectController(unittest.TestCase): 'Content-Type': 'text/plain', 'Content-Encoding': 'gzip'}) req.body = 'VERIFY TWO' - resp = self.object_controller.PUT(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 201) objfile = os.path.join(self.testdir, 'sda1', storage_directory(object_server.DATADIR, 'p', @@ -444,7 +444,7 @@ class TestObjectController(unittest.TestCase): headers={'X-Timestamp': normalize_timestamp(time()), 'Content-Type': 'text/plain'}) req.body = 'test' - resp = self.object_controller.PUT(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 201) def test_PUT_invalid_etag(self): @@ -453,7 +453,7 @@ class TestObjectController(unittest.TestCase): 'Content-Type': 'text/plain', 'ETag': 'invalid'}) req.body = 'test' - resp = self.object_controller.PUT(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 422) def test_PUT_user_metadata(self): @@ -465,7 +465,7 @@ class TestObjectController(unittest.TestCase): 'X-Object-Meta-1': 'One', 'X-Object-Meta-Two': 'Two'}) req.body = 'VERIFY THREE' - resp = self.object_controller.PUT(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 201) objfile = os.path.join(self.testdir, 'sda1', storage_directory(object_server.DATADIR, 'p', @@ -551,12 +551,12 @@ class TestObjectController(unittest.TestCase): def test_HEAD(self): """ Test swift.object_server.ObjectController.HEAD """ - req = Request.blank('/sda1/p/a/c') - resp = self.object_controller.HEAD(req) + req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'}) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 400) - req = Request.blank('/sda1/p/a/c/o') - resp = self.object_controller.HEAD(req) + req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'HEAD'}) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 404) timestamp = normalize_timestamp(time()) @@ -566,11 +566,11 @@ class TestObjectController(unittest.TestCase): 'X-Object-Meta-1': 'One', 'X-Object-Meta-Two': 'Two'}) req.body = 'VERIFY' - resp = self.object_controller.PUT(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 201) - req = Request.blank('/sda1/p/a/c/o') - resp = self.object_controller.HEAD(req) + req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'HEAD'}) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 200) self.assertEquals(resp.content_length, 6) self.assertEquals(resp.content_type, 'application/x-test') @@ -587,8 +587,8 @@ class TestObjectController(unittest.TestCase): hash_path('a', 'c', 'o')), timestamp + '.data') os.unlink(objfile) - req = Request.blank('/sda1/p/a/c/o') - resp = self.object_controller.HEAD(req) + req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'HEAD'}) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 404) sleep(.00001) @@ -599,7 +599,7 @@ class TestObjectController(unittest.TestCase): 'Content-Type': 'application/octet-stream', 'Content-length': '6'}) req.body = 'VERIFY' - resp = self.object_controller.PUT(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 201) sleep(.00001) @@ -607,11 +607,11 @@ class TestObjectController(unittest.TestCase): req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'DELETE'}, headers={'X-Timestamp': timestamp}) - resp = self.object_controller.DELETE(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 204) - req = Request.blank('/sda1/p/a/c/o') - resp = self.object_controller.HEAD(req) + req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'HEAD'}) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 404) def test_HEAD_quarantine_zbyte(self): @@ -621,7 +621,7 @@ class TestObjectController(unittest.TestCase): headers={'X-Timestamp': timestamp, 'Content-Type': 'application/x-test'}) req.body = 'VERIFY' - resp = self.object_controller.PUT(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 201) file = diskfile.DiskFile(self.testdir, 'sda1', 'p', 'a', 'c', 'o', FakeLogger(), keep_data_fp=True) @@ -634,8 +634,8 @@ class TestObjectController(unittest.TestCase): diskfile.write_metadata(fp, metadata) self.assertEquals(os.listdir(file.datadir)[0], file_name) - req = Request.blank('/sda1/p/a/c/o') - resp = self.object_controller.HEAD(req) + req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'HEAD'}) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 404) quar_dir = os.path.join(self.testdir, 'sda1', 'quarantined', 'objects', @@ -644,12 +644,12 @@ class TestObjectController(unittest.TestCase): def test_GET(self): """ Test swift.object_server.ObjectController.GET """ - req = Request.blank('/sda1/p/a/c') - resp = self.object_controller.GET(req) + req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'GET'}) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 400) - req = Request.blank('/sda1/p/a/c/o') - resp = self.object_controller.GET(req) + req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'GET'}) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 404) timestamp = normalize_timestamp(time()) @@ -659,11 +659,11 @@ class TestObjectController(unittest.TestCase): 'X-Object-Meta-1': 'One', 'X-Object-Meta-Two': 'Two'}) req.body = 'VERIFY' - resp = self.object_controller.PUT(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 201) - req = Request.blank('/sda1/p/a/c/o') - resp = self.object_controller.GET(req) + req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'GET'}) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 200) self.assertEquals(resp.body, 'VERIFY') self.assertEquals(resp.content_length, 6) @@ -677,23 +677,23 @@ class TestObjectController(unittest.TestCase): self.assertEquals(resp.headers['x-object-meta-1'], 'One') self.assertEquals(resp.headers['x-object-meta-two'], 'Two') - req = Request.blank('/sda1/p/a/c/o') + req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'GET'}) req.range = 'bytes=1-3' - resp = self.object_controller.GET(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 206) self.assertEquals(resp.body, 'ERI') self.assertEquals(resp.headers['content-length'], '3') - req = Request.blank('/sda1/p/a/c/o') + req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'GET'}) req.range = 'bytes=1-' - resp = self.object_controller.GET(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 206) self.assertEquals(resp.body, 'ERIFY') self.assertEquals(resp.headers['content-length'], '5') - req = Request.blank('/sda1/p/a/c/o') + req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'GET'}) req.range = 'bytes=-2' - resp = self.object_controller.GET(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 206) self.assertEquals(resp.body, 'FY') self.assertEquals(resp.headers['content-length'], '2') @@ -703,8 +703,8 @@ class TestObjectController(unittest.TestCase): hash_path('a', 'c', 'o')), timestamp + '.data') os.unlink(objfile) - req = Request.blank('/sda1/p/a/c/o') - resp = self.object_controller.GET(req) + req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'GET'}) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 404) sleep(.00001) @@ -715,7 +715,7 @@ class TestObjectController(unittest.TestCase): 'Content-Type': 'application:octet-stream', 'Content-Length': '6'}) req.body = 'VERIFY' - resp = self.object_controller.PUT(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 201) sleep(.00001) @@ -723,11 +723,11 @@ class TestObjectController(unittest.TestCase): req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'DELETE'}, headers={'X-Timestamp': timestamp}) - resp = self.object_controller.DELETE(req) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 204) - req = Request.blank('/sda1/p/a/c/o') - resp = self.object_controller.GET(req) + req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'GET'}) + resp = req.get_response(self.object_controller) self.assertEquals(resp.status_int, 404) def test_GET_if_match(self):