def test_validate_destination(self): req = Request.blank('/v/a/c/o', headers={'destination': 'c/o2'}) src_cont, src_obj = constraints.check_destination_header(req) self.assertEqual(src_cont, 'c') self.assertEqual(src_obj, 'o2') req = Request.blank('/v/a/c/o', headers={'destination': 'c/subdir/o2'}) src_cont, src_obj = constraints.check_destination_header(req) self.assertEqual(src_cont, 'c') self.assertEqual(src_obj, 'subdir/o2') req = Request.blank('/v/a/c/o', headers={'destination': '/c/o2'}) src_cont, src_obj = constraints.check_destination_header(req) self.assertEqual(src_cont, 'c') self.assertEqual(src_obj, 'o2')
def test_validate_destination(self): req = Request.blank("/v/a/c/o", headers={"destination": "c/o2"}) src_cont, src_obj = constraints.check_destination_header(req) self.assertEqual(src_cont, "c") self.assertEqual(src_obj, "o2") req = Request.blank("/v/a/c/o", headers={"destination": "c/subdir/o2"}) src_cont, src_obj = constraints.check_destination_header(req) self.assertEqual(src_cont, "c") self.assertEqual(src_obj, "subdir/o2") req = Request.blank("/v/a/c/o", headers={"destination": "/c/o2"}) src_cont, src_obj = constraints.check_destination_header(req) self.assertEqual(src_cont, "c") self.assertEqual(src_obj, "o2")
def COPY(self, req): """HTTP COPY request handler.""" if not req.headers.get('Destination'): return HTTPPreconditionFailed(request=req, body='Destination header required') dest_account = self.account_name if 'Destination-Account' in req.headers: dest_account = req.headers.get('Destination-Account') dest_account = check_account_format(req, dest_account) req.headers['X-Copy-From-Account'] = self.account_name self.account_name = dest_account del req.headers['Destination-Account'] dest_container, dest_object = check_destination_header(req) source = '/%s/%s' % (self.container_name, self.object_name) self.container_name = dest_container self.object_name = dest_object # re-write the existing request as a PUT instead of creating a new one # since this one is already attached to the posthooklogger req.method = 'PUT' req.path_info = '/v1/%s/%s/%s' % \ (dest_account, dest_container, dest_object) req.headers['Content-Length'] = 0 req.headers['X-Copy-From'] = quote(source) del req.headers['Destination'] return self.PUT(req)
def object_request(self, req, version, account, container, obj, allow_versioned_writes): account_name = unquote(account) container_name = unquote(container) object_name = unquote(obj) container_info = None resp = None is_enabled = config_true_value(allow_versioned_writes) if req.method in ('PUT', 'DELETE'): container_info = get_container_info( req.environ, self.app) elif req.method == 'COPY' and 'Destination' in req.headers: if 'Destination-Account' in req.headers: account_name = req.headers.get('Destination-Account') account_name = check_account_format(req, account_name) container_name, object_name = check_destination_header(req) req.environ['PATH_INFO'] = "/%s/%s/%s/%s" % ( version, account_name, container_name, object_name) container_info = get_container_info( req.environ, self.app) if not container_info: return self.app # To maintain backwards compatibility, container version # location could be stored as sysmeta or not, need to check both. # If stored as sysmeta, check if middleware is enabled. If sysmeta # is not set, but versions property is set in container_info, then # for backwards compatibility feature is enabled. object_versions = container_info.get( 'sysmeta', {}).get('versions-location') if object_versions and isinstance(object_versions, unicode): object_versions = object_versions.encode('utf-8') elif not object_versions: object_versions = container_info.get('versions') # if allow_versioned_writes is not set in the configuration files # but 'versions' is configured, enable feature to maintain # backwards compatibility if not allow_versioned_writes and object_versions: is_enabled = True if is_enabled and object_versions: object_versions = unquote(object_versions) vw_ctx = VersionedWritesContext(self.app, self.logger) if req.method in ('PUT', 'COPY'): policy_idx = req.headers.get( 'X-Backend-Storage-Policy-Index', container_info['storage_policy']) resp = vw_ctx.handle_obj_versions_put( req, object_versions, object_name, policy_idx) else: # handle DELETE resp = vw_ctx.handle_obj_versions_delete( req, object_versions, account_name, container_name, object_name) if resp: return resp else: return self.app
def object_request(self, req, version, account, container, obj, allow_versioned_writes): account_name = unquote(account) container_name = unquote(container) object_name = unquote(obj) container_info = None resp = None is_enabled = config_true_value(allow_versioned_writes) if req.method in ('PUT', 'DELETE'): container_info = get_container_info( req.environ, self.app) elif req.method == 'COPY' and 'Destination' in req.headers: if 'Destination-Account' in req.headers: account_name = req.headers.get('Destination-Account') account_name = check_account_format(req, account_name) container_name, object_name = check_destination_header(req) req.environ['PATH_INFO'] = "/%s/%s/%s/%s" % ( version, account_name, container_name, object_name) container_info = get_container_info( req.environ, self.app) if not container_info: return self.app # To maintain backwards compatibility, container version # location could be stored as sysmeta or not, need to check both. # If stored as sysmeta, check if middleware is enabled. If sysmeta # is not set, but versions property is set in container_info, then # for backwards compatibility feature is enabled. object_versions = container_info.get( 'sysmeta', {}).get('versions-location') if object_versions and isinstance(object_versions, six.text_type): object_versions = object_versions.encode('utf-8') elif not object_versions: object_versions = container_info.get('versions') # if allow_versioned_writes is not set in the configuration files # but 'versions' is configured, enable feature to maintain # backwards compatibility if not allow_versioned_writes and object_versions: is_enabled = True if is_enabled and object_versions: object_versions = unquote(object_versions) vw_ctx = VersionedWritesContext(self.app, self.logger) if req.method in ('PUT', 'COPY'): policy_idx = req.headers.get( 'X-Backend-Storage-Policy-Index', container_info['storage_policy']) resp = vw_ctx.handle_obj_versions_put( req, object_versions, object_name, policy_idx) else: # handle DELETE resp = vw_ctx.handle_obj_versions_delete( req, object_versions, account_name, container_name, object_name) if resp: return resp else: return self.app
def test_validate_destination(self): req = Request.blank( '/v/a/c/o', headers={'destination': 'c/o2'}) src_cont, src_obj = constraints.check_destination_header(req) self.assertEqual(src_cont, 'c') self.assertEqual(src_obj, 'o2') req = Request.blank( '/v/a/c/o', headers={'destination': 'c/subdir/o2'}) src_cont, src_obj = constraints.check_destination_header(req) self.assertEqual(src_cont, 'c') self.assertEqual(src_obj, 'subdir/o2') req = Request.blank( '/v/a/c/o', headers={'destination': '/c/o2'}) src_cont, src_obj = constraints.check_destination_header(req) self.assertEqual(src_cont, 'c') self.assertEqual(src_obj, 'o2')
def object_request(self, req, api_version, account, container, obj, allow_versioned_writes): account_name = unquote(account) container_name = unquote(container) object_name = unquote(obj) container_info = None resp = None is_enabled = config_true_value(allow_versioned_writes) if req.method in ('PUT', 'DELETE'): container_info = get_container_info(req.environ, self.app) elif req.method == 'COPY' and 'Destination' in req.headers: if 'Destination-Account' in req.headers: account_name = req.headers.get('Destination-Account') account_name = check_account_format(req, account_name) container_name, object_name = check_destination_header(req) req.environ['PATH_INFO'] = "/%s/%s/%s/%s" % ( api_version, account_name, container_name, object_name) container_info = get_container_info(req.environ, self.app) if not container_info: return self.app # To maintain backwards compatibility, container version # location could be stored as sysmeta or not, need to check both. # If stored as sysmeta, check if middleware is enabled. If sysmeta # is not set, but versions property is set in container_info, then # for backwards compatibility feature is enabled. versions_cont = container_info.get('sysmeta', {}).get('versions-location') if not versions_cont: versions_cont = container_info.get('versions') # if allow_versioned_writes is not set in the configuration files # but 'versions' is configured, enable feature to maintain # backwards compatibility if not allow_versioned_writes and versions_cont: is_enabled = True if is_enabled and versions_cont: versions_cont = unquote(versions_cont).split('/')[0] vw_ctx = VersionedWritesContext(self.app, self.logger) if req.method in ('PUT', 'COPY'): resp = vw_ctx.handle_obj_versions_put(req, versions_cont, api_version, account_name, object_name) else: # handle DELETE resp = vw_ctx.handle_obj_versions_delete( req, versions_cont, api_version, account_name, container_name, object_name) if resp: return resp else: return self.app
def COPY(self): """ COPY handler on Proxy """ if not self.request.headers.get('Destination'): return HTTPPreconditionFailed(request=self.request, body='Destination header required') params = self.verify_access_to_storlet() self.gateway.augmentStorletRequest(self.request, params) self._validate_copy_request() dest_container, dest_object = check_destination_header(self.request) # re-write the existing request as a PUT instead of creating a new one # TODO(eranr): do we want a new sub_request or re-write existing one as # we do below. See proxy obj controller COPY. self.request.method = 'PUT' self.request.path_info = '/v1/%s/%s/%s' % \ (self.account, dest_container, dest_object) self.request.headers['Content-Length'] = 0 del self.request.headers['Destination'] return self.base_handle_copy_request(self.container, self.obj, dest_container, dest_object)
def __call__(self, req): try: (version, account, container, obj) = req.split_path(3, 4, True) except ValueError: return self.app # verify new quota headers are properly formatted if not obj and req.method in ('PUT', 'POST'): val = req.headers.get('X-Container-Meta-Quota-Bytes') if val and not val.isdigit(): return HTTPBadRequest(body='Invalid bytes quota.') val = req.headers.get('X-Container-Meta-Quota-Count') if val and not val.isdigit(): return HTTPBadRequest(body='Invalid count quota.') # check user uploads against quotas elif obj and req.method in ('PUT', 'COPY'): container_info = None if req.method == 'PUT': container_info = get_container_info( req.environ, self.app, swift_source='CQ') if req.method == 'COPY' and 'Destination' in req.headers: dest_account = account if 'Destination-Account' in req.headers: dest_account = req.headers.get('Destination-Account') dest_account = check_account_format(req, dest_account) dest_container, dest_object = check_destination_header(req) path_info = req.environ['PATH_INFO'] req.environ['PATH_INFO'] = "/%s/%s/%s/%s" % ( version, dest_account, dest_container, dest_object) try: container_info = get_container_info( req.environ, self.app, swift_source='CQ') finally: req.environ['PATH_INFO'] = path_info if not container_info or not is_success(container_info['status']): # this will hopefully 404 later return self.app if 'quota-bytes' in container_info.get('meta', {}) and \ 'bytes' in container_info and \ container_info['meta']['quota-bytes'].isdigit(): content_length = (req.content_length or 0) if 'x-copy-from' in req.headers or req.method == 'COPY': if 'x-copy-from' in req.headers: container, obj = check_copy_from_header(req) path = '/%s/%s/%s/%s' % (version, account, container, obj) object_info = get_object_info(req.environ, self.app, path) if not object_info or not object_info['length']: content_length = 0 else: content_length = int(object_info['length']) new_size = int(container_info['bytes']) + content_length if int(container_info['meta']['quota-bytes']) < new_size: return self.bad_response(req, container_info) if 'quota-count' in container_info.get('meta', {}) and \ 'object_count' in container_info and \ container_info['meta']['quota-count'].isdigit(): new_count = int(container_info['object_count']) + 1 if int(container_info['meta']['quota-count']) < new_count: return self.bad_response(req, container_info) return self.app