summaryrefslogtreecommitdiff
path: root/swift/common/middleware/s3api/controllers/obj.py
diff options
context:
space:
mode:
Diffstat (limited to 'swift/common/middleware/s3api/controllers/obj.py')
-rw-r--r--swift/common/middleware/s3api/controllers/obj.py150
1 files changed, 150 insertions, 0 deletions
diff --git a/swift/common/middleware/s3api/controllers/obj.py b/swift/common/middleware/s3api/controllers/obj.py
new file mode 100644
index 000000000..7017170a7
--- /dev/null
+++ b/swift/common/middleware/s3api/controllers/obj.py
@@ -0,0 +1,150 @@
+# Copyright (c) 2010-2014 OpenStack Foundation.
+#
+# 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.
+
+import sys
+
+from swift.common.http import HTTP_OK, HTTP_PARTIAL_CONTENT, HTTP_NO_CONTENT
+from swift.common.swob import Range, content_range_header_value
+from swift.common.utils import public
+
+from swift.common.middleware.s3api.utils import S3Timestamp
+from swift.common.middleware.s3api.controllers.base import Controller
+from swift.common.middleware.s3api.s3response import S3NotImplemented, \
+ InvalidRange, NoSuchKey, InvalidArgument
+
+
+class ObjectController(Controller):
+ """
+ Handles requests on objects
+ """
+ def _gen_head_range_resp(self, req_range, resp):
+ """
+ Swift doesn't handle Range header for HEAD requests.
+ So, this method generates HEAD range response from HEAD response.
+ S3 return HEAD range response, if the value of range satisfies the
+ conditions which are described in the following document.
+ - http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35
+ """
+ length = long(resp.headers.get('Content-Length'))
+
+ try:
+ content_range = Range(req_range)
+ except ValueError:
+ return resp
+
+ ranges = content_range.ranges_for_length(length)
+ if ranges == []:
+ raise InvalidRange()
+ elif ranges:
+ if len(ranges) == 1:
+ start, end = ranges[0]
+ resp.headers['Content-Range'] = \
+ content_range_header_value(start, end, length)
+ resp.headers['Content-Length'] = (end - start)
+ resp.status = HTTP_PARTIAL_CONTENT
+ return resp
+ else:
+ # TODO: It is necessary to confirm whether need to respond to
+ # multi-part response.(e.g. bytes=0-10,20-30)
+ pass
+
+ return resp
+
+ def GETorHEAD(self, req):
+ resp = req.get_response(self.app)
+
+ if req.method == 'HEAD':
+ resp.app_iter = None
+
+ for key in ('content-type', 'content-language', 'expires',
+ 'cache-control', 'content-disposition',
+ 'content-encoding'):
+ if 'response-' + key in req.params:
+ resp.headers[key] = req.params['response-' + key]
+
+ return resp
+
+ @public
+ def HEAD(self, req):
+ """
+ Handle HEAD Object request
+ """
+ resp = self.GETorHEAD(req)
+
+ if 'range' in req.headers:
+ req_range = req.headers['range']
+ resp = self._gen_head_range_resp(req_range, resp)
+
+ return resp
+
+ @public
+ def GET(self, req):
+ """
+ Handle GET Object request
+ """
+ return self.GETorHEAD(req)
+
+ @public
+ def PUT(self, req):
+ """
+ Handle PUT Object and PUT Object (Copy) request
+ """
+ # set X-Timestamp by s3api to use at copy resp body
+ req_timestamp = S3Timestamp.now()
+ req.headers['X-Timestamp'] = req_timestamp.internal
+ if all(h in req.headers
+ for h in ('X-Amz-Copy-Source', 'X-Amz-Copy-Source-Range')):
+ raise InvalidArgument('x-amz-copy-source-range',
+ req.headers['X-Amz-Copy-Source-Range'],
+ 'Illegal copy header')
+ req.check_copy_source(self.app)
+ resp = req.get_response(self.app)
+
+ if 'X-Amz-Copy-Source' in req.headers:
+ resp.append_copy_resp_body(req.controller_name,
+ req_timestamp.s3xmlformat)
+
+ # delete object metadata from response
+ for key in list(resp.headers.keys()):
+ if key.startswith('x-amz-meta-'):
+ del resp.headers[key]
+
+ resp.status = HTTP_OK
+ return resp
+
+ @public
+ def POST(self, req):
+ raise S3NotImplemented()
+
+ @public
+ def DELETE(self, req):
+ """
+ Handle DELETE Object request
+ """
+ try:
+ query = req.gen_multipart_manifest_delete_query(self.app)
+ req.headers['Content-Type'] = None # Ignore client content-type
+ resp = req.get_response(self.app, query=query)
+ if query and resp.status_int == HTTP_OK:
+ for chunk in resp.app_iter:
+ pass # drain the bulk-deleter response
+ resp.status = HTTP_NO_CONTENT
+ resp.body = ''
+ except NoSuchKey:
+ # expect to raise NoSuchBucket when the bucket doesn't exist
+ exc_type, exc_value, exc_traceback = sys.exc_info()
+ req.get_container_info(self.app)
+ raise exc_type, exc_value, exc_traceback
+ return resp