request: move status code check into swift3.Request
No longer needs to check the returned status code from Swift in the controllers. Change-Id: I5c6f6a0b594ea59a8678f42e00e976c8487f286a
This commit is contained in:
@@ -20,3 +20,7 @@ class S3Exception(Exception):
|
|||||||
|
|
||||||
class NotS3Request(S3Exception):
|
class NotS3Request(S3Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BadSwiftRequest(S3Exception):
|
||||||
|
pass
|
||||||
|
@@ -52,26 +52,19 @@ following for an SAIO setup::
|
|||||||
calling_format=boto.s3.connection.OrdinaryCallingFormat())
|
calling_format=boto.s3.connection.OrdinaryCallingFormat())
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from urllib import quote
|
|
||||||
from simplejson import loads
|
from simplejson import loads
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from swift.common.utils import get_logger
|
from swift.common.utils import get_logger
|
||||||
from swift.common.http import HTTP_OK, HTTP_CREATED, HTTP_ACCEPTED, \
|
from swift.common.http import HTTP_OK
|
||||||
HTTP_NO_CONTENT, HTTP_UNAUTHORIZED, HTTP_FORBIDDEN, HTTP_NOT_FOUND, \
|
|
||||||
HTTP_CONFLICT, HTTP_UNPROCESSABLE_ENTITY, is_success, \
|
|
||||||
HTTP_REQUEST_ENTITY_TOO_LARGE
|
|
||||||
from swift.common.middleware.acl import parse_acl, referrer_allowed
|
from swift.common.middleware.acl import parse_acl, referrer_allowed
|
||||||
|
|
||||||
from swift3.etree import fromstring, tostring, Element, SubElement
|
from swift3.etree import fromstring, tostring, Element, SubElement
|
||||||
from swift3.exception import NotS3Request
|
from swift3.exception import NotS3Request
|
||||||
from swift3.request import Request
|
from swift3.request import Request
|
||||||
from swift3.response import HTTPNoContent, HTTPOk, ErrorResponse, \
|
from swift3.response import HTTPOk, ErrorResponse, AccessDenied, \
|
||||||
AccessDenied, BucketAlreadyExists, BucketNotEmpty, EntityTooLarge, \
|
InternalError, InvalidArgument, MalformedACLError, MethodNotAllowed, \
|
||||||
InternalError, InvalidArgument, InvalidDigest, MalformedACLError, \
|
NoSuchKey, S3NotImplemented
|
||||||
MethodNotAllowed, NoSuchBucket, NoSuchKey, S3NotImplemented, \
|
|
||||||
SignatureDoesNotMatch
|
|
||||||
|
|
||||||
|
|
||||||
XMLNS_XSI = 'http://www.w3.org/2001/XMLSchema-instance'
|
XMLNS_XSI = 'http://www.w3.org/2001/XMLSchema-instance'
|
||||||
|
|
||||||
@@ -227,17 +220,7 @@ class ServiceController(Controller):
|
|||||||
"""
|
"""
|
||||||
Handle GET Service request
|
Handle GET Service request
|
||||||
"""
|
"""
|
||||||
req.query_string = 'format=json'
|
resp = req.get_response(self.app, query={'format': 'json'})
|
||||||
resp = req.get_response(self.app)
|
|
||||||
status = resp.status_int
|
|
||||||
|
|
||||||
if status != HTTP_OK:
|
|
||||||
if status == HTTP_UNAUTHORIZED:
|
|
||||||
raise SignatureDoesNotMatch()
|
|
||||||
if status == HTTP_FORBIDDEN:
|
|
||||||
raise AccessDenied()
|
|
||||||
else:
|
|
||||||
raise InternalError()
|
|
||||||
|
|
||||||
containers = loads(resp.body)
|
containers = loads(resp.body)
|
||||||
# we don't keep the creation time of a backet (s3cmd doesn't
|
# we don't keep the creation time of a backet (s3cmd doesn't
|
||||||
@@ -263,14 +246,7 @@ class BucketController(Controller):
|
|||||||
"""
|
"""
|
||||||
Handle HEAD Bucket (Get Metadata) request
|
Handle HEAD Bucket (Get Metadata) request
|
||||||
"""
|
"""
|
||||||
if req.query_string:
|
return req.get_response(self.app)
|
||||||
req.query_string = ''
|
|
||||||
|
|
||||||
resp = req.get_response(self.app)
|
|
||||||
if resp.status_int == HTTP_NO_CONTENT:
|
|
||||||
resp.status_int = HTTP_OK
|
|
||||||
|
|
||||||
return resp
|
|
||||||
|
|
||||||
def GET(self, req):
|
def GET(self, req):
|
||||||
"""
|
"""
|
||||||
@@ -283,26 +259,18 @@ class BucketController(Controller):
|
|||||||
max_keys = min(int(req.params.get('max-keys', MAX_BUCKET_LISTING)),
|
max_keys = min(int(req.params.get('max-keys', MAX_BUCKET_LISTING)),
|
||||||
MAX_BUCKET_LISTING)
|
MAX_BUCKET_LISTING)
|
||||||
|
|
||||||
req.query_string = 'format=json&limit=%s' % (max_keys + 1)
|
query = {
|
||||||
|
'format': 'json',
|
||||||
|
'limit': max_keys + 1,
|
||||||
|
}
|
||||||
if 'marker' in req.params:
|
if 'marker' in req.params:
|
||||||
req.query_string += '&marker=%s' % quote(req.params['marker'])
|
query.update({'marker': req.params['marker']})
|
||||||
if 'prefix' in req.params:
|
if 'prefix' in req.params:
|
||||||
req.query_string += '&prefix=%s' % quote(req.params['prefix'])
|
query.update({'prefix': req.params['prefix']})
|
||||||
if 'delimiter' in req.params:
|
if 'delimiter' in req.params:
|
||||||
req.query_string += '&delimiter=%s' % \
|
query.update({'delimiter': req.params['delimiter']})
|
||||||
quote(req.params['delimiter'])
|
|
||||||
resp = req.get_response(self.app)
|
|
||||||
status = resp.status_int
|
|
||||||
|
|
||||||
if status != HTTP_OK:
|
resp = req.get_response(self.app, query=query)
|
||||||
if status == HTTP_UNAUTHORIZED:
|
|
||||||
raise SignatureDoesNotMatch()
|
|
||||||
if status == HTTP_FORBIDDEN:
|
|
||||||
raise AccessDenied()
|
|
||||||
elif status == HTTP_NOT_FOUND:
|
|
||||||
raise NoSuchBucket(req.container_name)
|
|
||||||
else:
|
|
||||||
raise InternalError()
|
|
||||||
|
|
||||||
objects = loads(resp.body)
|
objects = loads(resp.body)
|
||||||
|
|
||||||
@@ -360,40 +328,16 @@ class BucketController(Controller):
|
|||||||
req.headers[header] = acl
|
req.headers[header] = acl
|
||||||
|
|
||||||
resp = req.get_response(self.app)
|
resp = req.get_response(self.app)
|
||||||
status = resp.status_int
|
resp.status = HTTP_OK
|
||||||
|
resp.headers.update({'Location': req.container_name})
|
||||||
|
|
||||||
if status != HTTP_CREATED and status != HTTP_NO_CONTENT:
|
return resp
|
||||||
if status == HTTP_UNAUTHORIZED:
|
|
||||||
raise SignatureDoesNotMatch()
|
|
||||||
if status == HTTP_FORBIDDEN:
|
|
||||||
raise AccessDenied()
|
|
||||||
elif status == HTTP_ACCEPTED:
|
|
||||||
raise BucketAlreadyExists(req.container_name)
|
|
||||||
else:
|
|
||||||
raise InternalError()
|
|
||||||
|
|
||||||
return HTTPOk(headers={'Location': req.container_name})
|
|
||||||
|
|
||||||
def DELETE(self, req):
|
def DELETE(self, req):
|
||||||
"""
|
"""
|
||||||
Handle DELETE Bucket request
|
Handle DELETE Bucket request
|
||||||
"""
|
"""
|
||||||
resp = req.get_response(self.app)
|
return req.get_response(self.app)
|
||||||
status = resp.status_int
|
|
||||||
|
|
||||||
if status != HTTP_NO_CONTENT:
|
|
||||||
if status == HTTP_UNAUTHORIZED:
|
|
||||||
raise SignatureDoesNotMatch()
|
|
||||||
if status == HTTP_FORBIDDEN:
|
|
||||||
raise AccessDenied()
|
|
||||||
elif status == HTTP_NOT_FOUND:
|
|
||||||
raise NoSuchBucket(req.container_name)
|
|
||||||
elif status == HTTP_CONFLICT:
|
|
||||||
raise BucketNotEmpty()
|
|
||||||
else:
|
|
||||||
raise InternalError()
|
|
||||||
|
|
||||||
return HTTPNoContent()
|
|
||||||
|
|
||||||
def POST(self, req):
|
def POST(self, req):
|
||||||
"""
|
"""
|
||||||
@@ -408,21 +352,10 @@ class ObjectController(Controller):
|
|||||||
"""
|
"""
|
||||||
def GETorHEAD(self, req):
|
def GETorHEAD(self, req):
|
||||||
resp = req.get_response(self.app)
|
resp = req.get_response(self.app)
|
||||||
status = resp.status_int
|
|
||||||
|
|
||||||
if req.method == 'HEAD':
|
if req.method == 'HEAD':
|
||||||
resp.app_iter = None
|
resp.app_iter = None
|
||||||
|
|
||||||
if is_success(status):
|
|
||||||
return resp
|
return resp
|
||||||
elif status == HTTP_UNAUTHORIZED:
|
|
||||||
raise SignatureDoesNotMatch()
|
|
||||||
elif status == HTTP_FORBIDDEN:
|
|
||||||
raise AccessDenied()
|
|
||||||
elif status == HTTP_NOT_FOUND:
|
|
||||||
raise NoSuchKey(req.object_name)
|
|
||||||
else:
|
|
||||||
raise InternalError()
|
|
||||||
|
|
||||||
def HEAD(self, req):
|
def HEAD(self, req):
|
||||||
"""
|
"""
|
||||||
@@ -441,21 +374,6 @@ class ObjectController(Controller):
|
|||||||
Handle PUT Object and PUT Object (Copy) request
|
Handle PUT Object and PUT Object (Copy) request
|
||||||
"""
|
"""
|
||||||
resp = req.get_response(self.app)
|
resp = req.get_response(self.app)
|
||||||
status = resp.status_int
|
|
||||||
|
|
||||||
if status != HTTP_CREATED:
|
|
||||||
if status == HTTP_UNAUTHORIZED:
|
|
||||||
raise SignatureDoesNotMatch()
|
|
||||||
elif status == HTTP_FORBIDDEN:
|
|
||||||
raise AccessDenied()
|
|
||||||
elif status == HTTP_NOT_FOUND:
|
|
||||||
raise NoSuchBucket(req.container_name)
|
|
||||||
elif status == HTTP_UNPROCESSABLE_ENTITY:
|
|
||||||
raise InvalidDigest()
|
|
||||||
elif status == HTTP_REQUEST_ENTITY_TOO_LARGE:
|
|
||||||
raise EntityTooLarge()
|
|
||||||
else:
|
|
||||||
raise InternalError()
|
|
||||||
|
|
||||||
if 'HTTP_X_COPY_FROM' in req.environ:
|
if 'HTTP_X_COPY_FROM' in req.environ:
|
||||||
elem = Element('CopyObjectResult')
|
elem = Element('CopyObjectResult')
|
||||||
@@ -463,7 +381,9 @@ class ObjectController(Controller):
|
|||||||
body = tostring(elem, use_s3ns=False)
|
body = tostring(elem, use_s3ns=False)
|
||||||
return HTTPOk(body=body)
|
return HTTPOk(body=body)
|
||||||
|
|
||||||
return HTTPOk(etag=resp.etag)
|
resp.status = HTTP_OK
|
||||||
|
|
||||||
|
return resp
|
||||||
|
|
||||||
def POST(self, req):
|
def POST(self, req):
|
||||||
raise AccessDenied()
|
raise AccessDenied()
|
||||||
@@ -472,24 +392,7 @@ class ObjectController(Controller):
|
|||||||
"""
|
"""
|
||||||
Handle DELETE Object request
|
Handle DELETE Object request
|
||||||
"""
|
"""
|
||||||
try:
|
return req.get_response(self.app)
|
||||||
resp = req.get_response(self.app)
|
|
||||||
except Exception:
|
|
||||||
raise InternalError()
|
|
||||||
|
|
||||||
status = resp.status_int
|
|
||||||
|
|
||||||
if status != HTTP_NO_CONTENT:
|
|
||||||
if status == HTTP_UNAUTHORIZED:
|
|
||||||
raise SignatureDoesNotMatch()
|
|
||||||
elif status == HTTP_FORBIDDEN:
|
|
||||||
raise AccessDenied()
|
|
||||||
elif status == HTTP_NOT_FOUND:
|
|
||||||
raise NoSuchKey(req.object_name)
|
|
||||||
else:
|
|
||||||
raise InternalError()
|
|
||||||
|
|
||||||
return HTTPNoContent()
|
|
||||||
|
|
||||||
|
|
||||||
class AclController(Controller):
|
class AclController(Controller):
|
||||||
@@ -507,48 +410,9 @@ class AclController(Controller):
|
|||||||
"""
|
"""
|
||||||
Handles GET Bucket acl and GET Object acl.
|
Handles GET Bucket acl and GET Object acl.
|
||||||
"""
|
"""
|
||||||
if req.object_name:
|
resp = req.get_response(self.app, method='HEAD')
|
||||||
# Handle Object ACL
|
|
||||||
|
|
||||||
# ACL requests need to make a HEAD call rather than GET
|
return get_acl(req.access_key, resp.headers)
|
||||||
req.method = 'HEAD'
|
|
||||||
req.script_name = ''
|
|
||||||
req.query_string = ''
|
|
||||||
|
|
||||||
resp = req.get_response(self.app)
|
|
||||||
status = resp.status_int
|
|
||||||
headers = resp.headers
|
|
||||||
|
|
||||||
if is_success(status):
|
|
||||||
# Method must be GET or the body wont be returned to the caller
|
|
||||||
req.environ['REQUEST_METHOD'] = 'GET'
|
|
||||||
return get_acl(req.access_key, headers)
|
|
||||||
elif status == HTTP_UNAUTHORIZED:
|
|
||||||
raise SignatureDoesNotMatch()
|
|
||||||
elif status == HTTP_FORBIDDEN:
|
|
||||||
raise AccessDenied()
|
|
||||||
elif status == HTTP_NOT_FOUND:
|
|
||||||
raise NoSuchKey(req.object_name)
|
|
||||||
else:
|
|
||||||
raise InternalError()
|
|
||||||
|
|
||||||
else:
|
|
||||||
# Handle Bucket ACL
|
|
||||||
resp = req.get_response(self.app)
|
|
||||||
status = resp.status_int
|
|
||||||
headers = resp.headers
|
|
||||||
|
|
||||||
if is_success(status):
|
|
||||||
return get_acl(req.access_key, headers)
|
|
||||||
|
|
||||||
if status == HTTP_UNAUTHORIZED:
|
|
||||||
raise SignatureDoesNotMatch()
|
|
||||||
elif status == HTTP_FORBIDDEN:
|
|
||||||
raise AccessDenied()
|
|
||||||
elif status == HTTP_NOT_FOUND:
|
|
||||||
raise NoSuchBucket(req.container_name)
|
|
||||||
else:
|
|
||||||
raise InternalError()
|
|
||||||
|
|
||||||
def PUT(self, req):
|
def PUT(self, req):
|
||||||
"""
|
"""
|
||||||
@@ -568,20 +432,12 @@ class AclController(Controller):
|
|||||||
raise MalformedACLError()
|
raise MalformedACLError()
|
||||||
for header, acl in translated_acl:
|
for header, acl in translated_acl:
|
||||||
req.headers[header] = acl
|
req.headers[header] = acl
|
||||||
req.method = 'POST'
|
|
||||||
|
|
||||||
resp = req.get_response(self.app)
|
resp = req.get_response(self.app)
|
||||||
status = resp.status_int
|
resp.status = HTTP_OK
|
||||||
|
resp.headers.update({'Location': req.container_name})
|
||||||
|
|
||||||
if status != HTTP_ACCEPTED:
|
return resp
|
||||||
if status == HTTP_UNAUTHORIZED:
|
|
||||||
raise SignatureDoesNotMatch()
|
|
||||||
elif status == HTTP_FORBIDDEN:
|
|
||||||
raise AccessDenied()
|
|
||||||
else:
|
|
||||||
raise InternalError()
|
|
||||||
|
|
||||||
return HTTPOk(headers={'Location': req.container_name})
|
|
||||||
|
|
||||||
|
|
||||||
class LocationController(Controller):
|
class LocationController(Controller):
|
||||||
@@ -593,18 +449,7 @@ class LocationController(Controller):
|
|||||||
"""
|
"""
|
||||||
Handles GET Bucket location.
|
Handles GET Bucket location.
|
||||||
"""
|
"""
|
||||||
resp = req.get_response(self.app)
|
req.get_response(self.app, method='HEAD')
|
||||||
status = resp.status_int
|
|
||||||
|
|
||||||
if status != HTTP_OK:
|
|
||||||
if status == HTTP_UNAUTHORIZED:
|
|
||||||
raise SignatureDoesNotMatch()
|
|
||||||
elif status == HTTP_FORBIDDEN:
|
|
||||||
raise AccessDenied()
|
|
||||||
elif status == HTTP_NOT_FOUND:
|
|
||||||
raise NoSuchBucket(req.container_name)
|
|
||||||
else:
|
|
||||||
raise InternalError()
|
|
||||||
|
|
||||||
elem = Element('LocationConstraint')
|
elem = Element('LocationConstraint')
|
||||||
if self.conf['location'] != 'US':
|
if self.conf['location'] != 'US':
|
||||||
@@ -627,18 +472,7 @@ class LoggingStatusController(Controller):
|
|||||||
"""
|
"""
|
||||||
Handles GET Bucket logging.
|
Handles GET Bucket logging.
|
||||||
"""
|
"""
|
||||||
resp = req.get_response(self.app)
|
req.get_response(self.app, method='HEAD')
|
||||||
status = resp.status_int
|
|
||||||
|
|
||||||
if status != HTTP_OK:
|
|
||||||
if status == HTTP_UNAUTHORIZED:
|
|
||||||
raise SignatureDoesNotMatch()
|
|
||||||
elif status == HTTP_FORBIDDEN:
|
|
||||||
raise AccessDenied()
|
|
||||||
elif status == HTTP_NOT_FOUND:
|
|
||||||
raise NoSuchBucket(req.container_name)
|
|
||||||
else:
|
|
||||||
raise InternalError()
|
|
||||||
|
|
||||||
# logging disabled
|
# logging disabled
|
||||||
elem = Element('BucketLoggingStatus')
|
elem = Element('BucketLoggingStatus')
|
||||||
@@ -679,15 +513,10 @@ class MultiObjectDeleteController(Controller):
|
|||||||
# TODO: delete the specific version of the object
|
# TODO: delete the specific version of the object
|
||||||
raise S3NotImplemented()
|
raise S3NotImplemented()
|
||||||
|
|
||||||
sub_req = Request(req.environ.copy())
|
req.object_name = key
|
||||||
sub_req.query_string = ''
|
|
||||||
sub_req.content_length = 0
|
|
||||||
sub_req.method = 'DELETE'
|
|
||||||
sub_req.object_name = key
|
|
||||||
|
|
||||||
controller = ObjectController(self.app, self.conf)
|
|
||||||
try:
|
try:
|
||||||
controller.DELETE(sub_req)
|
req.get_response(self.app, method='DELETE')
|
||||||
except NoSuchKey:
|
except NoSuchKey:
|
||||||
pass
|
pass
|
||||||
except ErrorResponse as e:
|
except ErrorResponse as e:
|
||||||
@@ -791,18 +620,7 @@ class VersioningController(Controller):
|
|||||||
"""
|
"""
|
||||||
Handles GET Bucket versioning.
|
Handles GET Bucket versioning.
|
||||||
"""
|
"""
|
||||||
resp = req.get_response(self.app)
|
req.get_response(self.app, method='HEAD')
|
||||||
status = resp.status_int
|
|
||||||
|
|
||||||
if status != HTTP_OK:
|
|
||||||
if status == HTTP_UNAUTHORIZED:
|
|
||||||
raise SignatureDoesNotMatch()
|
|
||||||
elif status == HTTP_FORBIDDEN:
|
|
||||||
raise AccessDenied()
|
|
||||||
elif status == HTTP_NOT_FOUND:
|
|
||||||
raise NoSuchBucket(req.container_name)
|
|
||||||
else:
|
|
||||||
raise InternalError()
|
|
||||||
|
|
||||||
# Just report there is no versioning configured here.
|
# Just report there is no versioning configured here.
|
||||||
elem = Element('VersioningConfiguration')
|
elem = Element('VersioningConfiguration')
|
||||||
|
@@ -19,10 +19,20 @@ import email.utils
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from swift.common import swob
|
from swift.common import swob
|
||||||
|
from swift.common.http import HTTP_OK, HTTP_CREATED, HTTP_ACCEPTED, \
|
||||||
|
HTTP_NO_CONTENT, HTTP_UNAUTHORIZED, HTTP_FORBIDDEN, HTTP_NOT_FOUND, \
|
||||||
|
HTTP_CONFLICT, HTTP_UNPROCESSABLE_ENTITY, HTTP_REQUEST_ENTITY_TOO_LARGE, \
|
||||||
|
HTTP_PARTIAL_CONTENT, HTTP_NOT_MODIFIED, HTTP_PRECONDITION_FAILED, \
|
||||||
|
HTTP_REQUESTED_RANGE_NOT_SATISFIABLE, HTTP_LENGTH_REQUIRED, \
|
||||||
|
HTTP_BAD_REQUEST, HTTP_SERVICE_UNAVAILABLE
|
||||||
|
|
||||||
from swift3.response import AccessDenied, InvalidArgument, InvalidDigest, \
|
from swift3.response import AccessDenied, InvalidArgument, InvalidDigest, \
|
||||||
RequestTimeTooSkewed, Response, SignatureDoesNotMatch
|
RequestTimeTooSkewed, Response, SignatureDoesNotMatch, \
|
||||||
from swift3.exception import NotS3Request
|
ServiceUnavailable, BucketAlreadyExists, BucketNotEmpty, EntityTooLarge, \
|
||||||
|
InternalError, NoSuchBucket, NoSuchKey, PreconditionFailed, InvalidRange, \
|
||||||
|
MissingContentLength
|
||||||
|
|
||||||
|
from swift3.exception import NotS3Request, BadSwiftRequest
|
||||||
|
|
||||||
# List of sub-resources that must be maintained as part of the HMAC
|
# List of sub-resources that must be maintained as part of the HMAC
|
||||||
# signature string.
|
# signature string.
|
||||||
@@ -192,7 +202,7 @@ class Request(swob.Request):
|
|||||||
|
|
||||||
return ServiceController
|
return ServiceController
|
||||||
|
|
||||||
def to_swift_req(self):
|
def to_swift_req(self, method, query=None):
|
||||||
"""
|
"""
|
||||||
Create a Swift request based on this request's environment.
|
Create a Swift request based on this request's environment.
|
||||||
"""
|
"""
|
||||||
@@ -208,6 +218,8 @@ class Request(swob.Request):
|
|||||||
del env[key]
|
del env[key]
|
||||||
|
|
||||||
env['swift.source'] = 'S3'
|
env['swift.source'] = 'S3'
|
||||||
|
if method is not None:
|
||||||
|
env['REQUEST_METHOD'] = method
|
||||||
env['HTTP_X_AUTH_TOKEN'] = self.token
|
env['HTTP_X_AUTH_TOKEN'] = self.token
|
||||||
|
|
||||||
if self.object_name:
|
if self.object_name:
|
||||||
@@ -219,16 +231,160 @@ class Request(swob.Request):
|
|||||||
path = '/v1/%s' % (self.access_key)
|
path = '/v1/%s' % (self.access_key)
|
||||||
env['PATH_INFO'] = path
|
env['PATH_INFO'] = path
|
||||||
|
|
||||||
env['QUERY_STRING'] = self.query_string
|
query_string = ''
|
||||||
|
if query is not None:
|
||||||
|
params = []
|
||||||
|
for key, value in sorted(query.items()):
|
||||||
|
if value is not None:
|
||||||
|
params.append('%s=%s' % (key, quote(str(value))))
|
||||||
|
else:
|
||||||
|
params.append(key)
|
||||||
|
query_string = '&'.join(params)
|
||||||
|
env['QUERY_STRING'] = query_string
|
||||||
|
|
||||||
return swob.Request.blank(quote(path), environ=env)
|
return swob.Request.blank(quote(path), environ=env)
|
||||||
|
|
||||||
def get_response(self, app):
|
def _swift_success_codes(self, method):
|
||||||
|
"""
|
||||||
|
Returns a list of expected success codes from Swift.
|
||||||
|
"""
|
||||||
|
if self.container_name is None:
|
||||||
|
# Swift account access.
|
||||||
|
code_map = {
|
||||||
|
'GET': [
|
||||||
|
HTTP_OK,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
elif self.object_name is None:
|
||||||
|
# Swift container access.
|
||||||
|
code_map = {
|
||||||
|
'HEAD': [
|
||||||
|
HTTP_NO_CONTENT,
|
||||||
|
],
|
||||||
|
'GET': [
|
||||||
|
HTTP_OK,
|
||||||
|
HTTP_NO_CONTENT,
|
||||||
|
],
|
||||||
|
'PUT': [
|
||||||
|
HTTP_CREATED,
|
||||||
|
],
|
||||||
|
'POST': [
|
||||||
|
HTTP_NO_CONTENT,
|
||||||
|
],
|
||||||
|
'DELETE': [
|
||||||
|
HTTP_NO_CONTENT,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
# Swift object access.
|
||||||
|
code_map = {
|
||||||
|
'HEAD': [
|
||||||
|
HTTP_OK,
|
||||||
|
HTTP_PARTIAL_CONTENT,
|
||||||
|
HTTP_NOT_MODIFIED,
|
||||||
|
],
|
||||||
|
'GET': [
|
||||||
|
HTTP_OK,
|
||||||
|
HTTP_PARTIAL_CONTENT,
|
||||||
|
HTTP_NOT_MODIFIED,
|
||||||
|
],
|
||||||
|
'PUT': [
|
||||||
|
HTTP_CREATED,
|
||||||
|
],
|
||||||
|
'DELETE': [
|
||||||
|
HTTP_NO_CONTENT,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
return code_map[method]
|
||||||
|
|
||||||
|
def _swift_error_codes(self, method):
|
||||||
|
"""
|
||||||
|
Returns a dict from expected Swift error codes to the corresponding S3
|
||||||
|
error responses.
|
||||||
|
"""
|
||||||
|
if self.container_name is None:
|
||||||
|
# Swift account access.
|
||||||
|
code_map = {
|
||||||
|
'GET': {
|
||||||
|
},
|
||||||
|
}
|
||||||
|
elif self.object_name is None:
|
||||||
|
# Swift container access.
|
||||||
|
code_map = {
|
||||||
|
'HEAD': {
|
||||||
|
HTTP_NOT_FOUND: (NoSuchBucket, self.container_name),
|
||||||
|
},
|
||||||
|
'GET': {
|
||||||
|
HTTP_NOT_FOUND: (NoSuchBucket, self.container_name),
|
||||||
|
},
|
||||||
|
'PUT': {
|
||||||
|
HTTP_ACCEPTED: (BucketAlreadyExists, self.container_name),
|
||||||
|
},
|
||||||
|
'POST': {
|
||||||
|
HTTP_NOT_FOUND: (NoSuchBucket, self.container_name),
|
||||||
|
},
|
||||||
|
'DELETE': {
|
||||||
|
HTTP_NOT_FOUND: (NoSuchBucket, self.container_name),
|
||||||
|
HTTP_CONFLICT: BucketNotEmpty,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
# Swift object access.
|
||||||
|
code_map = {
|
||||||
|
'HEAD': {
|
||||||
|
HTTP_NOT_FOUND: (NoSuchKey, self.object_name),
|
||||||
|
HTTP_PRECONDITION_FAILED: PreconditionFailed,
|
||||||
|
},
|
||||||
|
'GET': {
|
||||||
|
HTTP_NOT_FOUND: (NoSuchKey, self.object_name),
|
||||||
|
HTTP_PRECONDITION_FAILED: PreconditionFailed,
|
||||||
|
HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: InvalidRange,
|
||||||
|
},
|
||||||
|
'PUT': {
|
||||||
|
HTTP_NOT_FOUND: (NoSuchBucket, self.container_name),
|
||||||
|
HTTP_UNPROCESSABLE_ENTITY: InvalidDigest,
|
||||||
|
HTTP_REQUEST_ENTITY_TOO_LARGE: EntityTooLarge,
|
||||||
|
HTTP_LENGTH_REQUIRED: MissingContentLength,
|
||||||
|
},
|
||||||
|
'DELETE': {
|
||||||
|
HTTP_NOT_FOUND: (NoSuchKey, self.object_name),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return code_map[method]
|
||||||
|
|
||||||
|
def get_response(self, app, method=None, query=None):
|
||||||
"""
|
"""
|
||||||
Calls the application with this request's environment. Returns a
|
Calls the application with this request's environment. Returns a
|
||||||
Response object that wraps up the application's result.
|
Response object that wraps up the application's result.
|
||||||
"""
|
"""
|
||||||
sw_req = self.to_swift_req()
|
method = method or self.environ['REQUEST_METHOD']
|
||||||
|
sw_req = self.to_swift_req(method=method, query=query)
|
||||||
sw_resp = sw_req.get_response(app)
|
sw_resp = sw_req.get_response(app)
|
||||||
|
resp = Response.from_swift_resp(sw_resp)
|
||||||
|
status = resp.status_int # pylint: disable-msg=E1101
|
||||||
|
|
||||||
return Response.from_swift_resp(sw_resp)
|
success_codes = self._swift_success_codes(method)
|
||||||
|
error_codes = self._swift_error_codes(method)
|
||||||
|
|
||||||
|
if status in success_codes:
|
||||||
|
return resp
|
||||||
|
|
||||||
|
if status in error_codes:
|
||||||
|
err_resp = error_codes[sw_resp.status_int]
|
||||||
|
if isinstance(err_resp, tuple):
|
||||||
|
raise err_resp[0](*err_resp[1:])
|
||||||
|
else:
|
||||||
|
raise err_resp()
|
||||||
|
|
||||||
|
if status == HTTP_BAD_REQUEST:
|
||||||
|
raise BadSwiftRequest(resp.body)
|
||||||
|
if status == HTTP_UNAUTHORIZED:
|
||||||
|
raise SignatureDoesNotMatch()
|
||||||
|
if status == HTTP_FORBIDDEN:
|
||||||
|
raise AccessDenied()
|
||||||
|
if status == HTTP_SERVICE_UNAVAILABLE:
|
||||||
|
raise ServiceUnavailable()
|
||||||
|
|
||||||
|
raise InternalError('unexpteted status code %d' % status)
|
||||||
|
@@ -88,6 +88,8 @@ class TestSwift3(unittest.TestCase):
|
|||||||
for b in self.objects:
|
for b in self.objects:
|
||||||
json_out.append(json_pattern % b)
|
json_out.append(json_pattern % b)
|
||||||
object_list = '[' + ','.join(json_out) + ']'
|
object_list = '[' + ','.join(json_out) + ']'
|
||||||
|
self.swift.register('HEAD', '/v1/AUTH_test/junk', swob.HTTPNoContent,
|
||||||
|
{}, object_list)
|
||||||
self.swift.register('GET', '/v1/AUTH_test/junk', swob.HTTPOk, {},
|
self.swift.register('GET', '/v1/AUTH_test/junk', swob.HTTPOk, {},
|
||||||
object_list)
|
object_list)
|
||||||
|
|
||||||
@@ -111,7 +113,7 @@ class TestSwift3(unittest.TestCase):
|
|||||||
self.swift.register('PUT', '/v1/AUTH_test/bucket',
|
self.swift.register('PUT', '/v1/AUTH_test/bucket',
|
||||||
swob.HTTPCreated, {}, None)
|
swob.HTTPCreated, {}, None)
|
||||||
self.swift.register('POST', '/v1/AUTH_test/bucket',
|
self.swift.register('POST', '/v1/AUTH_test/bucket',
|
||||||
swob.HTTPAccepted, {}, None)
|
swob.HTTPNoContent, {}, None)
|
||||||
self.swift.register('DELETE', '/v1/AUTH_test/bucket',
|
self.swift.register('DELETE', '/v1/AUTH_test/bucket',
|
||||||
swob.HTTPNoContent, {}, None)
|
swob.HTTPNoContent, {}, None)
|
||||||
|
|
||||||
@@ -543,6 +545,11 @@ class TestSwift3(unittest.TestCase):
|
|||||||
self.assertEquals(status.split()[0], '204')
|
self.assertEquals(status.split()[0], '204')
|
||||||
|
|
||||||
def test_object_multi_DELETE(self):
|
def test_object_multi_DELETE(self):
|
||||||
|
self.swift.register('DELETE', '/v1/AUTH_test/bucket/Key1',
|
||||||
|
swob.HTTPNoContent, {}, None)
|
||||||
|
self.swift.register('DELETE', '/v1/AUTH_test/bucket/Key2',
|
||||||
|
swob.HTTPNotFound, {}, None)
|
||||||
|
|
||||||
elem = Element('Delete')
|
elem = Element('Delete')
|
||||||
for key in ['Key1', 'Key2']:
|
for key in ['Key1', 'Key2']:
|
||||||
obj = SubElement(elem, 'Object')
|
obj = SubElement(elem, 'Object')
|
||||||
|
Reference in New Issue
Block a user