diff --git a/swift/account/server.py b/swift/account/server.py index b0d73bd172..eaa62e1cf9 100644 --- a/swift/account/server.py +++ b/swift/account/server.py @@ -36,6 +36,7 @@ from swift.common.utils import get_logger, get_param, hash_path, \ from swift.common.constraints import ACCOUNT_LISTING_LIMIT, \ check_mount, check_float, check_utf8 from swift.common.db_replicator import ReplicatorRpc +from swift.common.http import HTTPInsufficientStorage DATADIR = 'accounts' @@ -70,7 +71,7 @@ class AccountController(object): return HTTPBadRequest(body=str(err), content_type='text/plain', request=req) if self.mount_check and not check_mount(self.root, drive): - return Response(status='507 %s is not mounted' % drive) + return HTTPInsufficientStorage(drive=drive, request=req) if 'x-timestamp' not in req.headers or \ not check_float(req.headers['x-timestamp']): return HTTPBadRequest(body='Missing timestamp', request=req, @@ -90,7 +91,7 @@ class AccountController(object): return HTTPBadRequest(body=str(err), content_type='text/plain', request=req) if self.mount_check and not check_mount(self.root, drive): - return Response(status='507 %s is not mounted' % drive) + return HTTPInsufficientStorage(drive=drive, request=req) broker = self._get_account_broker(drive, part, account) if container: # put account container if 'x-trans-id' in req.headers: @@ -149,7 +150,7 @@ class AccountController(object): return HTTPBadRequest(body=str(err), content_type='text/plain', request=req) if self.mount_check and not check_mount(self.root, drive): - return Response(status='507 %s is not mounted' % drive) + return HTTPInsufficientStorage(drive=drive, request=req) broker = self._get_account_broker(drive, part, account) if not container: broker.pending_timeout = 0.1 @@ -180,7 +181,7 @@ class AccountController(object): return HTTPBadRequest(body=str(err), content_type='text/plain', request=req) if self.mount_check and not check_mount(self.root, drive): - return Response(status='507 %s is not mounted' % drive) + return HTTPInsufficientStorage(drive=drive, request=req) broker = self._get_account_broker(drive, part, account) broker.pending_timeout = 0.1 broker.stale_reads_ok = True @@ -274,7 +275,7 @@ class AccountController(object): request=req) drive, partition, hash = post_args if self.mount_check and not check_mount(self.root, drive): - return Response(status='507 %s is not mounted' % drive) + return HTTPInsufficientStorage(drive=drive, request=req) try: args = simplejson.load(req.environ['wsgi.input']) except ValueError, err: @@ -295,7 +296,7 @@ class AccountController(object): return HTTPBadRequest(body='Missing or bad timestamp', request=req, content_type='text/plain') if self.mount_check and not check_mount(self.root, drive): - return Response(status='507 %s is not mounted' % drive) + return HTTPInsufficientStorage(drive=drive, request=req) broker = self._get_account_broker(drive, part, account) if broker.is_deleted(): return HTTPNotFound(request=req) diff --git a/swift/common/bench.py b/swift/common/bench.py index 21085d8691..9ceedad59c 100644 --- a/swift/common/bench.py +++ b/swift/common/bench.py @@ -24,6 +24,7 @@ from eventlet.green.httplib import CannotSendRequest from swift.common.utils import TRUE_VALUES from swift.common import client from swift.common import direct_client +from swift.common.http import HTTP_CONFLICT class ConnectionPool(eventlet.pools.Pool): @@ -152,7 +153,7 @@ class BenchDELETE(Bench): try: client.delete_container(self.url, self.token, container) except client.ClientException, e: - if e.http_status != 409: + if e.http_status != HTTP_CONFLICT: self._log_status("Unable to delete container '%s'. " \ "Got http status '%d'." % (container, e.http_status)) diff --git a/swift/common/direct_client.py b/swift/common/direct_client.py index 76f686acfa..24042594c8 100644 --- a/swift/common/direct_client.py +++ b/swift/common/direct_client.py @@ -28,6 +28,8 @@ from eventlet import sleep, Timeout from swift.common.bufferedhttp import http_connect from swift.common.client import ClientException, json_loads from swift.common.utils import normalize_timestamp +from swift.common.http import HTTP_NO_CONTENT, HTTP_INSUFFICIENT_STORAGE, \ + is_success, is_server_error def quote(value, safe='/'): @@ -69,7 +71,7 @@ def direct_get_account(node, part, account, marker=None, limit=None, 'GET', path, query_string=qs) with Timeout(response_timeout): resp = conn.getresponse() - if resp.status < 200 or resp.status >= 300: + if not is_success(resp.status): resp.read() raise ClientException( 'Account server %s:%s direct GET %s gave status %s' % (node['ip'], @@ -81,7 +83,7 @@ def direct_get_account(node, part, account, marker=None, limit=None, resp_headers = {} for header, value in resp.getheaders(): resp_headers[header.lower()] = value - if resp.status == 204: + if resp.status == HTTP_NO_CONTENT: resp.read() return resp_headers, [] return resp_headers, json_loads(resp.read()) @@ -108,7 +110,7 @@ def direct_head_container(node, part, account, container, conn_timeout=5, with Timeout(response_timeout): resp = conn.getresponse() resp.read() - if resp.status < 200 or resp.status >= 300: + if not is_success(resp.status): raise ClientException( 'Container server %s:%s direct HEAD %s gave status %s' % (node['ip'], node['port'], @@ -157,7 +159,7 @@ def direct_get_container(node, part, account, container, marker=None, 'GET', path, query_string=qs) with Timeout(response_timeout): resp = conn.getresponse() - if resp.status < 200 or resp.status >= 300: + if not is_success(resp.status): resp.read() raise ClientException( 'Container server %s:%s direct GET %s gave stats %s' % (node['ip'], @@ -169,7 +171,7 @@ def direct_get_container(node, part, account, container, marker=None, resp_headers = {} for header, value in resp.getheaders(): resp_headers[header.lower()] = value - if resp.status == 204: + if resp.status == HTTP_NO_CONTENT: resp.read() return resp_headers, [] return resp_headers, json_loads(resp.read()) @@ -185,7 +187,7 @@ def direct_delete_container(node, part, account, container, conn_timeout=5, with Timeout(response_timeout): resp = conn.getresponse() resp.read() - if resp.status < 200 or resp.status >= 300: + if not is_success(resp.status): raise ClientException( 'Container server %s:%s direct DELETE %s gave status %s' % (node['ip'], node['port'], @@ -218,7 +220,7 @@ def direct_head_object(node, part, account, container, obj, conn_timeout=5, with Timeout(response_timeout): resp = conn.getresponse() resp.read() - if resp.status < 200 or resp.status >= 300: + if not is_success(resp.status): raise ClientException( 'Object server %s:%s direct HEAD %s gave status %s' % (node['ip'], node['port'], @@ -256,7 +258,7 @@ def direct_get_object(node, part, account, container, obj, conn_timeout=5, 'GET', path, headers=headers) with Timeout(response_timeout): resp = conn.getresponse() - if resp.status < 200 or resp.status >= 300: + if not is_success(resp.status): resp.read() raise ClientException( 'Object server %s:%s direct GET %s gave status %s' % @@ -326,7 +328,7 @@ def direct_put_object(node, part, account, container, name, contents, with Timeout(response_timeout): resp = conn.getresponse() resp.read() - if resp.status < 200 or resp.status >= 300: + if not is_success(resp.status): raise ClientException( 'Object server %s:%s direct PUT %s gave status %s' % (node['ip'], node['port'], @@ -361,7 +363,7 @@ def direct_post_object(node, part, account, container, name, headers, with Timeout(response_timeout): resp = conn.getresponse() resp.read() - if resp.status < 200 or resp.status >= 300: + if not is_success(resp.status): raise ClientException( 'Object server %s:%s direct POST %s gave status %s' % (node['ip'], node['port'], @@ -394,7 +396,7 @@ def direct_delete_object(node, part, account, container, obj, with Timeout(response_timeout): resp = conn.getresponse() resp.read() - if resp.status < 200 or resp.status >= 300: + if not is_success(resp.status): raise ClientException( 'Object server %s:%s direct DELETE %s gave status %s' % (node['ip'], node['port'], @@ -440,8 +442,8 @@ def retry(func, *args, **kwargs): except ClientException, err: if error_log: error_log(err) - if attempts > retries or err.http_status < 500 or \ - err.http_status == 507 or err.http_status > 599: + if attempts > retries or not is_server_error(err.http_status) or \ + err.http_status == HTTP_INSUFFICIENT_STORAGE: raise sleep(backoff) backoff *= 2 diff --git a/swift/common/http.py b/swift/common/http.py new file mode 100644 index 0000000000..a20fe4b92a --- /dev/null +++ b/swift/common/http.py @@ -0,0 +1,193 @@ +# Copyright (c) 2010-2012 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. + +from webob.exc import HTTPClientError,\ + HTTPInsufficientStorage as BaseHTTPInsufficientStorage + + +class HTTPClientDisconnect(HTTPClientError): + """ + subclass of :class:`~HTTPClientError` + + This code is introduced to log the case when the connection is closed by + client while HTTP server is processing its request + + code: 499, title: Client Disconnect + """ + code = 499 + title = 'Client Disconnect' + explanation = ( + 'This code is introduced to log the case when the connection ' + 'is closed by client while HTTP server is processing its request') + + +class HTTPInsufficientStorage(BaseHTTPInsufficientStorage): + """ + subclass of :class:`~HTTPInsufficientStorage` + + The server is unable to store the representation needed to + complete the request. + + code: 507, title: Insufficient Storage + """ + def __init__(self, drive=None, *args, **kwargs): + if drive: + self.explanation = ('%s is not mounted' % drive) + super(HTTPInsufficientStorage, self).__init__(*args, **kwargs) + + +def is_informational(status): + """ + Check if HTTP status code is informational. + + :param status: http status code + :returns: True if status is successful, else False + """ + return 100 <= status and status <= 199 + + +def is_success(status): + """ + Check if HTTP status code is successful. + + :param status: http status code + :returns: True if status is successful, else False + """ + return 200 <= status and status <= 299 + + +def is_redirection(status): + """ + Check if HTTP status code is redirection. + + :param status: http status code + :returns: True if status is redirection, else False + """ + return 300 <= status and status <= 399 + + +def is_client_error(status): + """ + Check if HTTP status code is client error. + + :param status: http status code + :returns: True if status is client error, else False + """ + return 400 <= status and status <= 499 + + +def is_server_error(status): + """ + Check if HTTP status code is server error. + + :param status: http status code + :returns: True if status is server error, else False + """ + return 500 <= status and status <= 599 + + +# List of HTTP status codes + +############################################################################### +## 1xx Informational +############################################################################### + +HTTP_CONTINUE = 100 +HTTP_SWITCHING_PROTOCOLS = 101 +HTTP_PROCESSING = 102 # WebDAV +HTTP_CHECKPOINT = 103 +HTTP_REQUEST_URI_TOO_LONG = 122 + +############################################################################### +## 2xx Success +############################################################################### + +HTTP_OK = 200 +HTTP_CREATED = 201 +HTTP_ACCEPTED = 202 +HTTP_NON_AUTHORITATIVE_INFORMATION = 203 +HTTP_NO_CONTENT = 204 +HTTP_RESET_CONTENT = 205 +HTTP_PARTIAL_CONTENT = 206 +HTTP_MULTI_STATUS = 207 # WebDAV +HTTP_IM_USED = 226 + +############################################################################### +## 3xx Redirection +############################################################################### + +HTTP_MULTIPLE_CHOICES = 300 +HTTP_MOVED_PERMANENTLY = 301 +HTTP_FOUND = 302 +HTTP_SEE_OTHER = 303 +HTTP_NOT_MODIFIED = 304 +HTTP_USE_PROXY = 305 +HTTP_SWITCH_PROXY = 306 +HTTP_TEMPORARY_REDIRECT = 307 +HTTP_RESUME_INCOMPLETE = 308 + +############################################################################### +## 4xx Client Error +############################################################################### + +HTTP_BAD_REQUEST = 400 +HTTP_UNAUTHORIZED = 401 +HTTP_PAYMENT_REQUIRED = 402 +HTTP_FORBIDDEN = 403 +HTTP_NOT_FOUND = 404 +HTTP_METHOD_NOT_ALLOWED = 405 +HTTP_NOT_ACCEPTABLE = 406 +HTTP_PROXY_AUTHENTICATION_REQUIRED = 407 +HTTP_REQUEST_TIMEOUT = 408 +HTTP_CONFLICT = 409 +HTTP_GONE = 410 +HTTP_LENGTH_REQUIRED = 411 +HTTP_PRECONDITION_FAILED = 412 +HTTP_REQUEST_ENTITY_TOO_LARGE = 413 +HTTP_REQUEST_URI_TOO_LONG = 414 +HTTP_UNSUPPORTED_MEDIA_TYPE = 415 +HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416 +HTTP_EXPECTATION_FAILED = 417 +HTTP_IM_A_TEAPOT = 418 +HTTP_UNPROCESSABLE_ENTITY = 422 # WebDAV +HTTP_LOCKED = 423 # WebDAV +HTTP_FAILED_DEPENDENCY = 424 # WebDAV +HTTP_UNORDERED_COLLECTION = 425 +HTTP_UPGRADE_REQUIED = 426 +HTTP_PRECONDITION_REQUIRED = 428 +HTTP_TOO_MANY_REQUESTS = 429 +HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431 +HTTP_NO_RESPONSE = 444 +HTTP_RETRY_WITH = 449 +HTTP_BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS = 450 +HTTP_CLIENT_CLOSED_REQUEST = 499 + +############################################################################### +## 5xx Server Error +############################################################################### + +HTTP_INTERNAL_SERVER_ERROR = 500 +HTTP_NOT_IMPLEMENTED = 501 +HTTP_BAD_GATEWAY = 502 +HTTP_SERVICE_UNAVAILABLE = 503 +HTTP_GATEWAY_TIMEOUT = 504 +HTTP_VERSION_NOT_SUPPORTED = 505 +HTTP_VARIANT_ALSO_NEGOTIATES = 506 +HTTP_INSUFFICIENT_STORAGE = 507 # WebDAV +HTTP_BANDWIDTH_LIMIT_EXCEEDED = 509 +HTTP_NOT_EXTENDED = 510 +HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511 +HTTP_NETWORK_READ_TIMEOUT_ERROR = 598 # not used in RFC +HTTP_NETWORK_CONNECT_TIMEOUT_ERROR = 599 # not used in RFC diff --git a/swift/common/middleware/formpost.py b/swift/common/middleware/formpost.py index 8c69f0a649..4680cf8f6f 100644 --- a/swift/common/middleware/formpost.py +++ b/swift/common/middleware/formpost.py @@ -112,6 +112,7 @@ from urllib import quote, unquote from swift.common.utils import get_logger, streq_const_time from swift.common.wsgi import make_pre_authed_env +from swift.common.http import HTTP_BAD_REQUEST #: The size of data to read from the form at any given time. @@ -324,7 +325,7 @@ class FormPost(object): return self._translate_form(env, start_response, attrs['boundary']) except (FormInvalid, EOFError), err: - self._log_request(env, 400) + self._log_request(env, HTTP_BAD_REQUEST) body = 'FormPost: %s' % err start_response('400 Bad Request', (('Content-Type', 'text/plain'), diff --git a/swift/common/middleware/staticweb.py b/swift/common/middleware/staticweb.py index 40f48c8950..11157bff22 100644 --- a/swift/common/middleware/staticweb.py +++ b/swift/common/middleware/staticweb.py @@ -125,6 +125,7 @@ from swift.common.utils import cache_from_env, get_logger, human_readable, \ split_path, TRUE_VALUES from swift.common.wsgi import make_pre_authed_env, make_pre_authed_request, \ WSGIContext +from swift.common.http import is_success, is_redirection, HTTP_NOT_FOUND def quote(value, safe='/'): @@ -183,7 +184,7 @@ class _StaticWebContext(WSGIContext): '/%s/%s/%s/%s%s' % (self.version, self.account, self.container, self._get_status_int(), self._error), self.agent)) - if self._get_status_int() // 100 == 2: + if is_success(self._get_status_int()): start_response(save_response_status, self._response_headers, self._response_exc_info) return resp @@ -213,7 +214,7 @@ class _StaticWebContext(WSGIContext): resp = make_pre_authed_request(env, 'HEAD', '/%s/%s/%s' % (self.version, self.account, self.container), agent=self.agent).get_response(self.app) - if resp.status_int // 100 == 2: + if is_success(resp.status_int): self._index = \ resp.headers.get('x-container-meta-web-index', '').strip() self._error = \ @@ -249,7 +250,7 @@ class _StaticWebContext(WSGIContext): else: prefix = '' resp = self._app_call(tmp_env) - if self._get_status_int() // 100 != 2: + if not is_success(self._get_status_int()): return self._error_response(resp, env, start_response) listing = None body = ''.join(resp) @@ -363,9 +364,10 @@ class _StaticWebContext(WSGIContext): tmp_env['PATH_INFO'] += self._index resp = self._app_call(tmp_env) status_int = self._get_status_int() - if status_int == 404: + if status_int == HTTP_NOT_FOUND: return self._listing(env, start_response) - elif self._get_status_int() // 100 not in (2, 3): + elif not is_success(self._get_status_int()) or \ + not is_redirection(self._get_status_int()): return self._error_response(resp, env, start_response) start_response(self._response_status, self._response_headers, self._response_exc_info) @@ -384,16 +386,16 @@ class _StaticWebContext(WSGIContext): '%s StaticWeb' % env.get('HTTP_USER_AGENT') resp = self._app_call(tmp_env) status_int = self._get_status_int() - if status_int // 100 in (2, 3): + if is_success(status_int) or is_redirection(status_int): start_response(self._response_status, self._response_headers, self._response_exc_info) return resp - if status_int != 404: + if status_int != HTTP_NOT_FOUND: return self._error_response(resp, env, start_response) self._get_container_info(env) if not self._listings and not self._index: return self.app(env, start_response) - status_int = 404 + status_int = HTTP_NOT_FOUND if self._index: tmp_env = dict(env) tmp_env['HTTP_USER_AGENT'] = \ @@ -403,7 +405,7 @@ class _StaticWebContext(WSGIContext): tmp_env['PATH_INFO'] += self._index resp = self._app_call(tmp_env) status_int = self._get_status_int() - if status_int // 100 in (2, 3): + if is_success(status_int) or is_redirection(status_int): if env['PATH_INFO'][-1] != '/': resp = HTTPMovedPermanently( location=env['PATH_INFO'] + '/') @@ -412,7 +414,7 @@ class _StaticWebContext(WSGIContext): start_response(self._response_status, self._response_headers, self._response_exc_info) return resp - if status_int == 404: + if status_int == HTTP_NOT_FOUND: if env['PATH_INFO'][-1] != '/': tmp_env = make_pre_authed_env(env, 'GET', '/%s/%s/%s' % (self.version, self.account, @@ -422,7 +424,7 @@ class _StaticWebContext(WSGIContext): '=/&limit=1&prefix=%s' % quote(self.obj + '/') resp = self._app_call(tmp_env) body = ''.join(resp) - if self._get_status_int() // 100 != 2 or not body or \ + if not is_success(self._get_status_int()) or not body or \ not json.loads(body): resp = HTTPNotFound()(env, self._start_response) return self._error_response(resp, env, start_response) diff --git a/swift/common/middleware/swift3.py b/swift/common/middleware/swift3.py index d8b862c7a6..7458cd3c22 100644 --- a/swift/common/middleware/swift3.py +++ b/swift/common/middleware/swift3.py @@ -64,6 +64,9 @@ from simplejson import loads from swift.common.utils import split_path from swift.common.wsgi import WSGIContext +from swift.common.http import HTTP_OK, HTTP_CREATED, HTTP_ACCEPTED, \ + HTTP_NO_CONTENT, HTTP_BAD_REQUEST, HTTP_UNAUTHORIZED, HTTP_FORBIDDEN, \ + HTTP_NOT_FOUND, HTTP_CONFLICT, is_success MAX_BUCKET_LISTING = 1000 @@ -78,24 +81,24 @@ def get_err_response(code): """ error_table = { 'AccessDenied': - (403, 'Access denied'), + (HTTP_FORBIDDEN, 'Access denied'), 'BucketAlreadyExists': - (409, 'The requested bucket name is not available'), + (HTTP_CONFLICT, 'The requested bucket name is not available'), 'BucketNotEmpty': - (409, 'The bucket you tried to delete is not empty'), + (HTTP_CONFLICT, 'The bucket you tried to delete is not empty'), 'InvalidArgument': - (400, 'Invalid Argument'), + (HTTP_BAD_REQUEST, 'Invalid Argument'), 'InvalidBucketName': - (400, 'The specified bucket is not valid'), + (HTTP_BAD_REQUEST, 'The specified bucket is not valid'), 'InvalidURI': - (400, 'Could not parse the specified URI'), + (HTTP_BAD_REQUEST, 'Could not parse the specified URI'), 'NoSuchBucket': - (404, 'The specified bucket does not exist'), + (HTTP_NOT_FOUND, 'The specified bucket does not exist'), 'SignatureDoesNotMatch': - (403, 'The calculated request signature does not match '\ - 'your provided one'), + (HTTP_FORBIDDEN, 'The calculated request signature does not '\ + 'match your provided one'), 'NoSuchKey': - (404, 'The resource you requested does not exist')} + (HTTP_NOT_FOUND, 'The resource you requested does not exist')} resp = Response(content_type='text/xml') resp.status = error_table[code][0] @@ -173,8 +176,8 @@ class ServiceController(WSGIContext): body_iter = self._app_call(env) status = self._get_status_int() - if status != 200: - if status == 401: + if status != HTTP_OK: + if status == HTTP_UNAUTHORIZED: return get_err_response('AccessDenied') else: return get_err_response('InvalidURI') @@ -190,7 +193,8 @@ class ServiceController(WSGIContext): % ("".join(['%s' \ '2009-02-03T16:45:09.000Z' % xml_escape(i['name']) for i in containers])) - resp = Response(status=200, content_type='application/xml', body=body) + resp = Response(status=HTTP_OK, content_type='application/xml', + body=body) return resp @@ -226,10 +230,10 @@ class BucketController(WSGIContext): body_iter = self._app_call(env) status = self._get_status_int() - if status != 200: - if status == 401: + if status != HTTP_OK: + if status == HTTP_UNAUTHORIZED: return get_err_response('AccessDenied') - elif status == 404: + elif status == HTTP_NOT_FOUND: return get_err_response('NoSuchBucket') else: return get_err_response('InvalidURI') @@ -275,17 +279,17 @@ class BucketController(WSGIContext): body_iter = self._app_call(env) status = self._get_status_int() - if status != 201: - if status == 401: + if status != HTTP_CREATED: + if status == HTTP_UNAUTHORIZED: return get_err_response('AccessDenied') - elif status == 202: + elif status == HTTP_ACCEPTED: return get_err_response('BucketAlreadyExists') else: return get_err_response('InvalidURI') resp = Response() resp.headers.add('Location', self.container_name) - resp.status = 200 + resp.status = HTTP_OK return resp def DELETE(self, env, start_response): @@ -295,18 +299,18 @@ class BucketController(WSGIContext): body_iter = self._app_call(env) status = self._get_status_int() - if status != 204: - if status == 401: + if status != HTTP_NO_CONTENT: + if status == HTTP_UNAUTHORIZED: return get_err_response('AccessDenied') - elif status == 404: + elif status == HTTP_NOT_FOUND: return get_err_response('NoSuchBucket') - elif status == 409: + elif status == HTTP_CONFLICT: return get_err_response('BucketNotEmpty') else: return get_err_response('InvalidURI') resp = Response() - resp.status = 204 + resp.status = HTTP_NO_CONTENT return resp @@ -328,7 +332,7 @@ class ObjectController(WSGIContext): status = self._get_status_int() headers = dict(self._response_headers) - if 200 <= status < 300: + if is_success(status): if 'QUERY_STRING' in env: args = dict(urlparse.parse_qsl(env['QUERY_STRING'], 1)) else: @@ -346,9 +350,9 @@ class ObjectController(WSGIContext): 'etag', 'last-modified'): new_hdrs[key] = val return Response(status=status, headers=new_hdrs, app_iter=app_iter) - elif status == 401: + elif status == HTTP_UNAUTHORIZED: return get_err_response('AccessDenied') - elif status == 404: + elif status == HTTP_NOT_FOUND: return get_err_response('NoSuchKey') else: return get_err_response('InvalidURI') @@ -381,10 +385,10 @@ class ObjectController(WSGIContext): body_iter = self._app_call(env) status = self._get_status_int() - if status != 201: - if status == 401: + if status != HTTP_CREATED: + if status == HTTP_UNAUTHORIZED: return get_err_response('AccessDenied') - elif status == 404: + elif status == HTTP_NOT_FOUND: return get_err_response('NoSuchBucket') else: return get_err_response('InvalidURI') @@ -393,7 +397,7 @@ class ObjectController(WSGIContext): body = '' \ '"%s"' \ '' % self._response_header_value('etag') - return Response(status=200, body=body) + return Response(status=HTTP_OK, body=body) return Response(status=200, etag=self._response_header_value('etag')) @@ -404,16 +408,16 @@ class ObjectController(WSGIContext): body_iter = self._app_call(env) status = self._get_status_int() - if status != 204: - if status == 401: + if status != HTTP_NO_CONTENT: + if status == HTTP_UNAUTHORIZED: return get_err_response('AccessDenied') - elif status == 404: + elif status == HTTP_NOT_FOUND: return get_err_response('NoSuchKey') else: return get_err_response('InvalidURI') resp = Response() - resp.status = 204 + resp.status = HTTP_NO_CONTENT return resp diff --git a/swift/common/middleware/tempauth.py b/swift/common/middleware/tempauth.py index 1fdfd91a7a..417da9360a 100644 --- a/swift/common/middleware/tempauth.py +++ b/swift/common/middleware/tempauth.py @@ -29,6 +29,7 @@ from webob.exc import HTTPBadRequest, HTTPForbidden, HTTPNotFound, \ from swift.common.middleware.acl import clean_acl, parse_acl, referrer_allowed from swift.common.utils import cache_from_env, get_logger, get_remote_client, \ split_path, TRUE_VALUES +from swift.common.http import HTTP_CLIENT_CLOSED_REQUEST class TempAuth(object): @@ -458,7 +459,7 @@ class TempAuth(object): status_int = response.status_int if getattr(req, 'client_disconnect', False) or \ getattr(response, 'client_disconnect', False): - status_int = 499 + status_int = HTTP_CLIENT_CLOSED_REQUEST self.logger.info(' '.join(quote(str(x)) for x in (client or '-', req.remote_addr or '-', strftime('%d/%b/%Y/%H/%M/%S', gmtime()), req.method, the_request, req.environ['SERVER_PROTOCOL'], diff --git a/swift/common/middleware/tempurl.py b/swift/common/middleware/tempurl.py index a1467e818b..f581078f71 100644 --- a/swift/common/middleware/tempurl.py +++ b/swift/common/middleware/tempurl.py @@ -88,6 +88,7 @@ from urlparse import parse_qs from swift.common.utils import get_logger from swift.common.wsgi import make_pre_authed_env +from swift.common.http import HTTP_UNAUTHORIZED #: Default headers to remove from incoming requests. Simply a whitespace @@ -376,7 +377,7 @@ class TempURL(object): :param start_response: The WSGI start_response hook. :returns: 401 response as per WSGI. """ - self._log_request(env, 401) + self._log_request(env, HTTP_UNAUTHORIZED) body = '401 Unauthorized: Temp URL invalid\n' start_response('401 Unauthorized', [('Content-Type', 'text/plain'), diff --git a/swift/container/server.py b/swift/container/server.py index a3cfd5123f..ad355d6c4c 100644 --- a/swift/container/server.py +++ b/swift/container/server.py @@ -39,6 +39,8 @@ from swift.common.constraints import CONTAINER_LISTING_LIMIT, \ from swift.common.bufferedhttp import http_connect from swift.common.exceptions import ConnectionTimeout from swift.common.db_replicator import ReplicatorRpc +from swift.common.http import HTTP_NOT_FOUND, is_success, \ + HTTPInsufficientStorage DATADIR = 'containers' @@ -119,10 +121,9 @@ class ContainerController(object): with Timeout(self.node_timeout): account_response = conn.getresponse() account_response.read() - if account_response.status == 404: + if account_response.status == HTTP_NOT_FOUND: return HTTPNotFound(request=req) - elif account_response.status < 200 or \ - account_response.status > 299: + elif not is_success(account_response.status): self.logger.error(_('ERROR Account update failed ' 'with %(ip)s:%(port)s/%(device)s (will retry ' 'later): Response %(status)s %(reason)s'), @@ -150,7 +151,7 @@ class ContainerController(object): return HTTPBadRequest(body='Missing timestamp', request=req, content_type='text/plain') if self.mount_check and not check_mount(self.root, drive): - return Response(status='507 %s is not mounted' % drive) + return HTTPInsufficientStorage(drive=drive, request=req) broker = self._get_container_broker(drive, part, account, container) if account.startswith(self.auto_create_account_prefix) and obj and \ not os.path.exists(broker.db_file): @@ -195,7 +196,7 @@ class ContainerController(object): if err: return HTTPBadRequest(err) if self.mount_check and not check_mount(self.root, drive): - return Response(status='507 %s is not mounted' % drive) + return HTTPInsufficientStorage(drive=drive, request=req) timestamp = normalize_timestamp(req.headers['x-timestamp']) broker = self._get_container_broker(drive, part, account, container) if obj: # put container object @@ -245,7 +246,7 @@ class ContainerController(object): return HTTPBadRequest(body=str(err), content_type='text/plain', request=req) if self.mount_check and not check_mount(self.root, drive): - return Response(status='507 %s is not mounted' % drive) + return HTTPInsufficientStorage(drive=drive, request=req) broker = self._get_container_broker(drive, part, account, container) broker.pending_timeout = 0.1 broker.stale_reads_ok = True @@ -273,7 +274,7 @@ class ContainerController(object): return HTTPBadRequest(body=str(err), content_type='text/plain', request=req) if self.mount_check and not check_mount(self.root, drive): - return Response(status='507 %s is not mounted' % drive) + return HTTPInsufficientStorage(drive=drive, request=req) broker = self._get_container_broker(drive, part, account, container) broker.pending_timeout = 0.1 broker.stale_reads_ok = True @@ -388,7 +389,7 @@ class ContainerController(object): request=req) drive, partition, hash = post_args if self.mount_check and not check_mount(self.root, drive): - return Response(status='507 %s is not mounted' % drive) + return HTTPInsufficientStorage(drive=drive, request=req) try: args = simplejson.load(req.environ['wsgi.input']) except ValueError, err: @@ -414,7 +415,7 @@ class ContainerController(object): if err: return HTTPBadRequest(err) if self.mount_check and not check_mount(self.root, drive): - return Response(status='507 %s is not mounted' % drive) + return HTTPInsufficientStorage(drive=drive, request=req) broker = self._get_container_broker(drive, part, account, container) if broker.is_deleted(): return HTTPNotFound(request=req) diff --git a/swift/container/sync.py b/swift/container/sync.py index 441951d00f..9ddc68529d 100644 --- a/swift/container/sync.py +++ b/swift/container/sync.py @@ -30,6 +30,7 @@ from swift.common.db import ContainerBroker from swift.common.utils import audit_location_generator, get_logger, \ hash_path, normalize_timestamp, TRUE_VALUES, validate_sync_to, whataremyips from swift.common.daemon import Daemon +from swift.common.http import HTTP_UNAUTHORIZED, HTTP_NOT_FOUND class _Iter2FileLikeObject(object): @@ -350,7 +351,7 @@ class ContainerSync(Daemon): 'x-container-sync-key': sync_key}, proxy=self.proxy) except ClientException, err: - if err.http_status != 404: + if err.http_status != HTTP_NOT_FOUND: raise self.container_deletes += 1 else: @@ -377,7 +378,7 @@ class ContainerSync(Daemon): # non-404 one. We don't want to mistakenly assume the # object no longer exists just because one says so and # the others errored for some other reason. - if not exc or exc.http_status == 404: + if not exc or exc.http_status == HTTP_NOT_FOUND: exc = err if timestamp < looking_for_timestamp: if exc: @@ -399,13 +400,13 @@ class ContainerSync(Daemon): contents=_Iter2FileLikeObject(body), proxy=self.proxy) self.container_puts += 1 except ClientException, err: - if err.http_status == 401: + if err.http_status == HTTP_UNAUTHORIZED: self.logger.info(_('Unauth %(sync_from)r ' '=> %(sync_to)r'), {'sync_from': '%s/%s' % (quote(info['account']), quote(info['container'])), 'sync_to': sync_to}) - elif err.http_status == 404: + elif err.http_status == HTTP_NOT_FOUND: self.logger.info(_('Not found %(sync_from)r ' '=> %(sync_to)r'), {'sync_from': '%s/%s' % diff --git a/swift/container/updater.py b/swift/container/updater.py index 6a9fd1294a..ad37530c7a 100644 --- a/swift/container/updater.py +++ b/swift/container/updater.py @@ -31,6 +31,7 @@ from swift.common.exceptions import ConnectionTimeout from swift.common.ring import Ring from swift.common.utils import get_logger, whataremyips, TRUE_VALUES from swift.common.daemon import Daemon +from swift.common.http import is_success, HTTP_INTERNAL_SERVER_ERROR class ContainerUpdater(Daemon): @@ -214,7 +215,7 @@ class ContainerUpdater(Daemon): successes = 0 failures = 0 for event in events: - if 200 <= event.wait() < 300: + if is_success(event.wait()): successes += 1 else: failures += 1 @@ -265,7 +266,7 @@ class ContainerUpdater(Daemon): except (Exception, Timeout): self.logger.exception(_('ERROR account update failed with ' '%(ip)s:%(port)s/%(device)s (will retry later): '), node) - return 500 + return HTTP_INTERNAL_SERVER_ERROR with Timeout(self.node_timeout): try: resp = conn.getresponse() @@ -275,4 +276,4 @@ class ContainerUpdater(Daemon): if self.logger.getEffectiveLevel() <= logging.DEBUG: self.logger.exception( _('Exception with %(ip)s:%(port)s/%(device)s'), node) - return 500 + return HTTP_INTERNAL_SERVER_ERROR diff --git a/swift/obj/expirer.py b/swift/obj/expirer.py index d581d1bbae..676510525d 100644 --- a/swift/obj/expirer.py +++ b/swift/obj/expirer.py @@ -24,6 +24,8 @@ from webob import Request from swift.common.daemon import Daemon from swift.common.utils import get_logger +from swift.common.http import HTTP_NO_CONTENT, HTTP_NOT_FOUND, HTTP_CONFLICT, \ + HTTP_PRECONDITION_FAILED try: import simplejson as json @@ -161,8 +163,9 @@ class ObjectExpirer(Daemon): the hidden expiration account. """ resp = self.get_response('HEAD', - '/v1/' + quote(self.expiring_objects_account), {}, (2, 404)) - if resp.status_int == 404: + '/v1/' + quote(self.expiring_objects_account), {}, + (2, HTTP_NOT_FOUND)) + if resp.status_int == HTTP_NOT_FOUND: return (0, 0) return (int(resp.headers['x-account-container-count']), int(resp.headers['x-account-object-count'])) @@ -176,8 +179,8 @@ class ObjectExpirer(Daemon): marker = '' while True: resp = self.get_response('GET', path + '&marker=' + quote(marker), - {}, (2, 404)) - if resp.status_int in (204, 404): + {}, (2, HTTP_NOT_FOUND)) + if resp.status_int in (HTTP_NO_CONTENT, HTTP_NOT_FOUND): break data = json.loads(resp.body) if not data: @@ -198,8 +201,8 @@ class ObjectExpirer(Daemon): marker = '' while True: resp = self.get_response('GET', path + '&marker=' + quote(marker), - {}, (2, 404)) - if resp.status_int in (204, 404): + {}, (2, HTTP_NOT_FOUND)) + if resp.status_int in (HTTP_NO_CONTENT, HTTP_NOT_FOUND): break data = json.loads(resp.body) if not data: @@ -220,7 +223,8 @@ class ObjectExpirer(Daemon): perform the actual delete. """ self.get_response('DELETE', '/v1/%s' % (quote(actual_obj),), - {'X-If-Delete-At': str(timestamp)}, (2, 404, 412)) + {'X-If-Delete-At': str(timestamp)}, + (2, HTTP_NOT_FOUND, HTTP_PRECONDITION_FAILED)) def delete_object(self, container, obj): """ @@ -232,7 +236,7 @@ class ObjectExpirer(Daemon): self.get_response('DELETE', '/v1/%s/%s/%s' % (quote(self.expiring_objects_account), quote(container), quote(obj)), - {}, (2, 404)) + {}, (2, HTTP_NOT_FOUND)) def delete_container(self, container): """ @@ -243,4 +247,4 @@ class ObjectExpirer(Daemon): self.get_response('DELETE', '/v1/%s/%s' % (quote(self.expiring_objects_account), quote(container)), - {}, (2, 404, 409)) + {}, (2, HTTP_NOT_FOUND, HTTP_CONFLICT)) diff --git a/swift/obj/replicator.py b/swift/obj/replicator.py index 4d5e68c257..c56384b119 100644 --- a/swift/obj/replicator.py +++ b/swift/obj/replicator.py @@ -36,6 +36,7 @@ from swift.common.utils import whataremyips, unlink_older_than, lock_path, \ TRUE_VALUES from swift.common.bufferedhttp import http_connect from swift.common.daemon import Daemon +from swift.common.http import HTTP_OK, HTTP_INSUFFICIENT_STORAGE hubs.use_hub('poll') @@ -408,12 +409,12 @@ class ObjectReplicator(Daemon): resp = http_connect(node['ip'], node['port'], node['device'], job['partition'], 'REPLICATE', '', headers={'Content-Length': '0'}).getresponse() - if resp.status == 507: + if resp.status == HTTP_INSUFFICIENT_STORAGE: self.logger.error(_('%(ip)s/%(device)s responded' ' as unmounted'), node) attempts_left += 1 continue - if resp.status != 200: + if resp.status != HTTP_OK: self.logger.error(_("Invalid response %(resp)s " "from %(ip)s"), {'resp': resp.status, 'ip': node['ip']}) diff --git a/swift/obj/server.py b/swift/obj/server.py index 2770c22b87..f155732967 100644 --- a/swift/obj/server.py +++ b/swift/obj/server.py @@ -45,6 +45,8 @@ from swift.common.exceptions import ConnectionTimeout, DiskFileError, \ DiskFileNotExist from swift.obj.replicator import tpooled_get_hashes, invalidate_hash, \ quarantine_renamer +from swift.common.http import is_success, HTTPInsufficientStorage, \ + HTTPClientDisconnect DATADIR = 'objects' @@ -404,7 +406,7 @@ class ObjectController(object): with Timeout(self.node_timeout): response = conn.getresponse() response.read() - if 200 <= response.status < 300: + if is_success(response.status): return else: self.logger.error(_('ERROR Container update failed ' @@ -492,7 +494,7 @@ class ObjectController(object): return HTTPBadRequest(body='X-Delete-At in past', request=request, content_type='text/plain') if self.mount_check and not check_mount(self.devices, device): - return Response(status='507 %s is not mounted' % device) + return HTTPInsufficientStorage(drive=device, request=request) file = DiskFile(self.devices, device, partition, account, container, obj, self.logger, disk_chunk_size=self.disk_chunk_size) @@ -536,7 +538,7 @@ class ObjectController(object): return HTTPBadRequest(body=str(err), request=request, content_type='text/plain') if self.mount_check and not check_mount(self.devices, device): - return Response(status='507 %s is not mounted' % device) + return HTTPInsufficientStorage(drive=device, request=request) if 'x-timestamp' not in request.headers or \ not check_float(request.headers['x-timestamp']): return HTTPBadRequest(body='Missing timestamp', request=request, @@ -575,7 +577,7 @@ class ObjectController(object): if 'content-length' in request.headers and \ int(request.headers['content-length']) != upload_size: - return Response(status='499 Client Disconnect') + return HTTPClientDisconnect(request=request) etag = etag.hexdigest() if 'etag' in request.headers and \ request.headers['etag'].lower() != etag: @@ -625,7 +627,7 @@ class ObjectController(object): return HTTPBadRequest(body=str(err), request=request, content_type='text/plain') if self.mount_check and not check_mount(self.devices, device): - return Response(status='507 %s is not mounted' % device) + return HTTPInsufficientStorage(drive=device, request=request) file = DiskFile(self.devices, device, partition, account, container, obj, self.logger, keep_data_fp=True, disk_chunk_size=self.disk_chunk_size) @@ -701,7 +703,7 @@ class ObjectController(object): resp.body = str(err) return resp if self.mount_check and not check_mount(self.devices, device): - return Response(status='507 %s is not mounted' % device) + return HTTPInsufficientStorage(drive=device, request=request) file = DiskFile(self.devices, device, partition, account, container, obj, self.logger, disk_chunk_size=self.disk_chunk_size) if file.is_deleted() or ('X-Delete-At' in file.metadata and @@ -742,7 +744,7 @@ class ObjectController(object): return HTTPBadRequest(body='Missing timestamp', request=request, content_type='text/plain') if self.mount_check and not check_mount(self.devices, device): - return Response(status='507 %s is not mounted' % device) + return HTTPInsufficientStorage(drive=device, request=request) response_class = HTTPNoContent file = DiskFile(self.devices, device, partition, account, container, obj, self.logger, disk_chunk_size=self.disk_chunk_size) @@ -785,7 +787,7 @@ class ObjectController(object): return HTTPBadRequest(body=str(e), request=request, content_type='text/plain') if self.mount_check and not check_mount(self.devices, device): - return Response(status='507 %s is not mounted' % device) + return HTTPInsufficientStorage(drive=device, request=request) path = os.path.join(self.devices, device, DATADIR, partition) if not os.path.exists(path): mkdirs(path) diff --git a/swift/obj/updater.py b/swift/obj/updater.py index 844c10f963..9be1919f95 100644 --- a/swift/obj/updater.py +++ b/swift/obj/updater.py @@ -28,6 +28,8 @@ from swift.common.ring import Ring from swift.common.utils import get_logger, renamer, write_pickle from swift.common.daemon import Daemon from swift.obj.server import ASYNCDIR +from swift.common.http import is_success, HTTP_NOT_FOUND, \ + HTTP_INTERNAL_SERVER_ERROR class ObjectUpdater(Daemon): @@ -178,7 +180,7 @@ class ObjectUpdater(Daemon): if node['id'] not in successes: status = self.object_update(node, part, update['op'], obj, update['headers']) - if not (200 <= status < 300) and status != 404: + if not is_success(status) and status != HTTP_NOT_FOUND: success = False else: successes.append(node['id']) @@ -217,4 +219,4 @@ class ObjectUpdater(Daemon): except (Exception, Timeout): self.logger.exception(_('ERROR with remote server ' '%(ip)s:%(port)s/%(device)s'), node) - return 500 + return HTTP_INTERNAL_SERVER_ERROR diff --git a/swift/proxy/server.py b/swift/proxy/server.py index ba23e47a11..879282ae40 100644 --- a/swift/proxy/server.py +++ b/swift/proxy/server.py @@ -61,6 +61,12 @@ from swift.common.constraints import check_metadata, check_object_creation, \ from swift.common.exceptions import ChunkReadTimeout, \ ChunkWriteTimeout, ConnectionTimeout, ListingIterNotFound, \ ListingIterNotAuthorized, ListingIterError +from swift.common.http import is_informational, is_success, is_redirection, \ + is_client_error, is_server_error, HTTP_CONTINUE, HTTP_OK, HTTP_CREATED, \ + HTTP_ACCEPTED, HTTP_PARTIAL_CONTENT, HTTP_MULTIPLE_CHOICES, \ + HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_REQUESTED_RANGE_NOT_SATISFIABLE, \ + HTTP_CLIENT_CLOSED_REQUEST, HTTP_INTERNAL_SERVER_ERROR, \ + HTTP_SERVICE_UNAVAILABLE, HTTP_INSUFFICIENT_STORAGE, HTTPClientDisconnect def update_headers(response, headers): @@ -181,7 +187,7 @@ class SegmentedIterable(object): self.controller.iter_nodes(partition, nodes, self.controller.app.object_ring), path, self.controller.app.object_ring.replica_count) - if resp.status_int // 100 != 2: + if not is_success(resp.status_int): raise Exception(_('Could not load object segment %(path)s:' \ ' %(status)s') % {'path': path, 'status': resp.status_int}) self.segment_iter = resp.app_iter @@ -197,7 +203,7 @@ class SegmentedIterable(object): 'cont': self.controller.container_name, 'obj': self.controller.object_name}) err.swift_logged = True - self.response.status_int = 503 + self.response.status_int = HTTP_SERVICE_UNAVAILABLE raise def next(self): @@ -230,7 +236,7 @@ class SegmentedIterable(object): 'cont': self.controller.container_name, 'obj': self.controller.object_name}) err.swift_logged = True - self.response.status_int = 503 + self.response.status_int = HTTP_SERVICE_UNAVAILABLE raise def app_iter_range(self, start, stop): @@ -291,7 +297,7 @@ class SegmentedIterable(object): 'cont': self.controller.container_name, 'obj': self.controller.object_name}) err.swift_logged = True - self.response.status_int = 503 + self.response.status_int = HTTP_SERVICE_UNAVAILABLE raise @@ -402,9 +408,9 @@ class Controller(object): else: result_code = cache_value['status'] container_count = cache_value['container_count'] - if result_code == 200: + if result_code == HTTP_OK: return partition, nodes, container_count - elif result_code == 404 and not autocreate: + elif result_code == HTTP_NOT_FOUND and not autocreate: return None, None, None result_code = 0 container_count = 0 @@ -419,17 +425,17 @@ class Controller(object): with Timeout(self.app.node_timeout): resp = conn.getresponse() body = resp.read() - if 200 <= resp.status <= 299: - result_code = 200 + if is_success(resp.status): + result_code = HTTP_OK container_count = int( resp.getheader('x-account-container-count') or 0) break - elif resp.status == 404: + elif resp.status == HTTP_NOT_FOUND: if result_code == 0: - result_code = 404 - elif result_code != 404: + result_code = HTTP_NOT_FOUND + elif result_code != HTTP_NOT_FOUND: result_code = -1 - elif resp.status == 507: + elif resp.status == HTTP_INSUFFICIENT_STORAGE: self.error_limit(node) continue else: @@ -440,7 +446,7 @@ class Controller(object): except (Exception, Timeout): self.exception_occurred(node, _('Account'), _('Trying to get account info for %s') % path) - if result_code == 404 and autocreate: + if result_code == HTTP_NOT_FOUND and autocreate: if len(account) > MAX_ACCOUNT_NAME_LENGTH: return None, None, None headers = {'X-Timestamp': normalize_timestamp(time.time()), @@ -449,18 +455,18 @@ class Controller(object): resp = self.make_requests(Request.blank('/v1' + path), self.app.account_ring, partition, 'PUT', path, [headers] * len(nodes)) - if resp.status_int // 100 != 2: + if not is_success(resp.status_int): raise Exception('Could not autocreate account %r' % path) - result_code = 200 - if self.app.memcache and result_code in (200, 404): - if result_code == 200: + result_code = HTTP_OK + if self.app.memcache and result_code in (HTTP_OK, HTTP_NOT_FOUND): + if result_code == HTTP_OK: cache_timeout = self.app.recheck_account_existence else: cache_timeout = self.app.recheck_account_existence * 0.1 self.app.memcache.set(cache_key, {'status': result_code, 'container_count': container_count}, timeout=cache_timeout) - if result_code == 200: + if result_code == HTTP_OK: return partition, nodes, container_count return None, None, None @@ -488,10 +494,10 @@ class Controller(object): write_acl = cache_value['write_acl'] sync_key = cache_value.get('sync_key') versions = cache_value.get('versions') - if status == 200: + if status == HTTP_OK: return partition, nodes, read_acl, write_acl, sync_key, \ versions - elif status == 404: + elif status == HTTP_NOT_FOUND: return None, None, None, None, None, None if not self.account_info(account, autocreate=account_autocreate)[1]: return None, None, None, None, None, None @@ -511,8 +517,8 @@ class Controller(object): with Timeout(self.app.node_timeout): resp = conn.getresponse() body = resp.read() - if 200 <= resp.status <= 299: - result_code = 200 + if is_success(resp.status): + result_code = HTTP_OK read_acl = resp.getheader('x-container-read') write_acl = resp.getheader('x-container-write') sync_key = resp.getheader('x-container-sync-key') @@ -520,12 +526,12 @@ class Controller(object): resp.getheader('X-Container-Object-Count') versions = resp.getheader('x-versions-location') break - elif resp.status == 404: + elif resp.status == HTTP_NOT_FOUND: if result_code == 0: - result_code = 404 - elif result_code != 404: + result_code = HTTP_NOT_FOUND + elif result_code != HTTP_NOT_FOUND: result_code = -1 - elif resp.status == 507: + elif resp.status == HTTP_INSUFFICIENT_STORAGE: self.error_limit(node) continue else: @@ -536,8 +542,8 @@ class Controller(object): except (Exception, Timeout): self.exception_occurred(node, _('Container'), _('Trying to get container info for %s') % path) - if self.app.memcache and result_code in (200, 404): - if result_code == 200: + if self.app.memcache and result_code in (HTTP_OK, HTTP_NOT_FOUND): + if result_code == HTTP_OK: cache_timeout = self.app.recheck_container_existence else: cache_timeout = self.app.recheck_container_existence * 0.1 @@ -549,7 +555,7 @@ class Controller(object): 'container_size': container_size, 'versions': versions}, timeout=cache_timeout) - if result_code == 200: + if result_code == HTTP_OK: return partition, nodes, read_acl, write_acl, sync_key, versions return None, None, None, None, None, None @@ -579,9 +585,10 @@ class Controller(object): conn.node = node with Timeout(self.app.node_timeout): resp = conn.getresponse() - if 200 <= resp.status < 500: + if not is_informational(resp.status) and \ + not is_server_error(resp.status): return resp.status, resp.reason, resp.read() - elif resp.status == 507: + elif resp.status == HTTP_INSUFFICIENT_STORAGE: self.error_limit(node) except (Exception, Timeout): self.exception_occurred(node, self.server_type, @@ -606,7 +613,7 @@ class Controller(object): head, query_string) response = [resp for resp in pile if resp] while len(response) < ring.replica_count: - response.append((503, '', '')) + response.append((HTTP_SERVICE_UNAVAILABLE, '', '')) statuses, reasons, bodies = zip(*response) return self.best_response(req, statuses, reasons, bodies, '%s %s' % (self.server_type, req.method)) @@ -627,7 +634,7 @@ class Controller(object): """ resp = Response(request=req) if len(statuses): - for hundred in (200, 300, 400): + for hundred in (HTTP_OK, HTTP_MULTIPLE_CHOICES, HTTP_BAD_REQUEST): hstatuses = \ [s for s in statuses if hundred <= s < hundred + 100] if len(hstatuses) > len(statuses) / 2: @@ -782,13 +789,14 @@ class Controller(object): _('Trying to %(method)s %(path)s') % {'method': req.method, 'path': req.path}) continue - if possible_source.status == 507: + if possible_source.status == HTTP_INSUFFICIENT_STORAGE: self.error_limit(node) continue - if 200 <= possible_source.status <= 399: + if is_success(possible_source.status) or \ + is_redirection(possible_source.status): # 404 if we know we don't have a synced copy if not float(possible_source.getheader('X-PUT-Timestamp', 1)): - statuses.append(404) + statuses.append(HTTP_NOT_FOUND) reasons.append('') bodies.append('') possible_source.read() @@ -811,13 +819,14 @@ class Controller(object): statuses.append(possible_source.status) reasons.append(possible_source.reason) bodies.append(possible_source.read()) - if possible_source.status >= 500: + if is_server_error(possible_source.status): self.error_occurred(node, _('ERROR %(status)d %(body)s ' \ 'From %(type)s Server') % {'status': possible_source.status, 'body': bodies[-1][:1024], 'type': server_type}) if source: - if req.method == 'GET' and source.status in (200, 206): + if req.method == 'GET' and \ + source.status in (HTTP_OK, HTTP_PARTIAL_CONTENT): res = Response(request=req, conditional_response=True) res.bytes_transferred = 0 res.app_iter = self._make_app_iter(node, source, res) @@ -834,7 +843,7 @@ class Controller(object): res.charset = None res.content_type = source.getheader('Content-Type') return res - elif 200 <= source.status <= 399: + elif is_success(source.status) or is_redirection(source.status): res = status_map[source.status](request=req) update_headers(res, source.getheaders()) # Used by container sync feature @@ -883,9 +892,9 @@ class ObjectController(Controller): aresp = env['swift.authorize'](lreq) if aresp: raise ListingIterNotAuthorized(aresp) - if lresp.status_int == 404: + if lresp.status_int == HTTP_NOT_FOUND: raise ListingIterNotFound() - elif lresp.status_int // 100 != 2: + elif not is_success(lresp.status_int): raise ListingIterError() if not lresp.body: break @@ -914,7 +923,7 @@ class ObjectController(Controller): # If we get a 416 Requested Range Not Satisfiable we have to check if # we were actually requesting a manifest and then redo # the range request on the whole object. - if resp.status_int == 416: + if resp.status_int == HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: req_range = req.range req.range = None resp2 = self.GETorHEAD_base(req, _('Object'), partition, @@ -1022,7 +1031,7 @@ class ObjectController(Controller): # Older editions returned 202 Accepted on object POSTs, so we'll # convert any 201 Created responses to that for compatibility with # picky clients. - if resp.status_int != 201: + if resp.status_int != HTTP_CREATED: return resp return HTTPAccepted(request=req) else: @@ -1098,10 +1107,10 @@ class ObjectController(Controller): node['device'], part, 'PUT', path, headers) with Timeout(self.app.node_timeout): resp = conn.getexpect() - if resp.status == 100: + if resp.status == HTTP_CONTINUE: conn.node = node return conn - elif resp.status == 507: + elif resp.status == HTTP_INSUFFICIENT_STORAGE: self.error_limit(node) except: self.exception_occurred(node, _('Object'), @@ -1183,7 +1192,7 @@ class ObjectController(Controller): if object_versions and not req.environ.get('swift_versioned_copy'): is_manifest = 'x-object-manifest' in req.headers or \ 'x-object-manifest' in hresp.headers - if hresp.status_int != 404 and not is_manifest: + if hresp.status_int != HTTP_NOT_FOUND and not is_manifest: # This is a version manifest and needs to be handled # differently. First copy the existing data to a new object, # then write the data from this request to the version manifest @@ -1206,10 +1215,10 @@ class ObjectController(Controller): copy_req = Request.blank(req.path_info, headers=copy_headers, environ=copy_environ) copy_resp = self.COPY(copy_req) - if copy_resp.status_int // 100 == 4: + if is_client_error(copy_resp.status_int): # missing container or bad permissions return HTTPPreconditionFailed(request=req) - elif copy_resp.status_int // 100 != 2: + elif not is_success(copy_resp.status_int): # could not copy the data, bail return HTTPServiceUnavailable(request=req) @@ -1238,7 +1247,7 @@ class ObjectController(Controller): self.object_name = src_obj_name self.container_name = src_container_name source_resp = self.GET(source_req) - if source_resp.status_int >= 300: + if source_resp.status_int >= HTTP_MULTIPLE_CHOICES: return source_resp self.object_name = orig_obj_name self.container_name = orig_container_name @@ -1332,12 +1341,12 @@ class ObjectController(Controller): req.client_disconnect = True self.app.logger.exception( _('ERROR Exception causing client disconnect')) - return Response(status='499 Client Disconnect') + return HTTPClientDisconnect(request=req) if req.content_length and req.bytes_transferred < req.content_length: req.client_disconnect = True self.app.logger.warn( _('Client disconnected without sending enough data')) - return Response(status='499 Client Disconnect') + return HTTPClientDisconnect(request=req) statuses = [] reasons = [] bodies = [] @@ -1349,12 +1358,12 @@ class ObjectController(Controller): statuses.append(response.status) reasons.append(response.reason) bodies.append(response.read()) - if response.status >= 500: + if response.status >= HTTP_INTERNAL_SERVER_ERROR: self.error_occurred(conn.node, _('ERROR %(status)d %(body)s From Object Server ' \ 're: %(path)s') % {'status': response.status, 'body': bodies[-1][:1024], 'path': req.path}) - elif 200 <= response.status < 300: + elif is_success(response.status): etags.add(response.getheader('etag').strip('"')) except (Exception, Timeout): self.exception_occurred(conn.node, _('Object'), @@ -1365,7 +1374,7 @@ class ObjectController(Controller): return HTTPServerError(request=req) etag = len(etags) and etags.pop() or None while len(statuses) < len(nodes): - statuses.append(503) + statuses.append(HTTP_SERVICE_UNAVAILABLE) reasons.append('') bodies.append('') resp = self.best_response(req, statuses, reasons, bodies, @@ -1426,10 +1435,10 @@ class ObjectController(Controller): creq = Request.blank(copy_path, headers=copy_headers, environ=copy_environ) copy_resp = self.COPY(creq) - if copy_resp.status_int // 100 == 4: + if is_client_error(copy_resp.status_int): # some user error, maybe permissions return HTTPPreconditionFailed(request=req) - elif copy_resp.status_int // 100 != 2: + elif not is_success(copy_resp.status_int): # could not copy the data, bail return HTTPServiceUnavailable(request=req) # reset these because the COPY changed them @@ -1666,7 +1675,8 @@ class ContainerController(Controller): self.app.memcache.delete(cache_key) resp = self.make_requests(req, self.app.container_ring, container_partition, 'DELETE', req.path_info, headers) - if resp.status_int == 202: # Indicates no server had the container + # Indicates no server had the container + if resp.status_int == HTTP_ACCEPTED: return HTTPNotFound(request=req) return resp @@ -1685,7 +1695,7 @@ class AccountController(Controller): shuffle(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 resp.status_int == HTTP_NOT_FOUND 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' % \ @@ -1698,7 +1708,7 @@ class AccountController(Controller): Request.blank('/v1/' + self.account_name), self.app.account_ring, partition, 'PUT', '/' + self.account_name, [headers] * len(nodes)) - if resp.status_int // 100 != 2: + if not is_success(resp.status_int): raise Exception('Could not autocreate account %r' % self.account_name) resp = self.GETorHEAD_base(req, _('Account'), partition, nodes, @@ -1746,7 +1756,7 @@ class AccountController(Controller): 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 resp.status_int == HTTP_NOT_FOUND 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' % \ @@ -1756,7 +1766,7 @@ class AccountController(Controller): 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: + if not is_success(resp.status_int): raise Exception('Could not autocreate account %r' % self.account_name) return resp @@ -1996,7 +2006,7 @@ class Application(BaseApplication): status_int = response.status_int if getattr(req, 'client_disconnect', False) or \ getattr(response, 'client_disconnect', False): - status_int = 499 + status_int = HTTP_CLIENT_CLOSED_REQUEST self.access_logger.info(' '.join(quote(str(x)) for x in ( client or '-', req.remote_addr or '-',