From 7698544370c9bd0e4fb4092de9090b363c1b9eae Mon Sep 17 00:00:00 2001 From: Sofia Papagiannaki Date: Fri, 24 Jun 2011 19:02:04 +0300 Subject: [PATCH] extend api to create an object by uploading the hashmap instead of actual data - enable format parameter in PUT requests on objects - when format is json the api instead of actual data expects a json formatted dictionary (similar to one returned by extended GET responses) containing information about the block identities - backend update_object_hashmap() now checks for the block existence before performing the update - incase of missing data Conflict 409 is returned containing the list of the missing block identites --- pithos/api/functions.py | 58 +++++++++++++++++++++++++++++++-------------- pithos/backends/simple.py | 10 ++++++++ 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/pithos/api/functions.py b/pithos/api/functions.py index 035092d..8610ac9 100644 --- a/pithos/api/functions.py +++ b/pithos/api/functions.py @@ -544,7 +544,7 @@ def object_read(request, v_account, v_container, v_object): return object_data_response(request, sizes, hashmaps, meta) -@api_method('PUT') +@api_method('PUT', format_allowed=True) def object_write(request, v_account, v_container, v_object): # Normal Response Codes: 201 # Error Response Codes: serviceUnavailable (503), @@ -554,7 +554,6 @@ def object_write(request, v_account, v_container, v_object): # itemNotFound (404), # unauthorized (401), # badRequest (400) - copy_from = request.META.get('HTTP_X_COPY_FROM') move_from = request.META.get('HTTP_X_MOVE_FROM') if copy_from or move_from: @@ -584,26 +583,49 @@ def object_write(request, v_account, v_container, v_object): if 'Content-Type' not in meta: raise LengthRequired('Missing Content-Type header') - md5 = hashlib.md5() - size = 0 - hashmap = [] - sock = raw_input_socket(request) - for data in socket_read_iterator(sock, content_length, backend.block_size): - # TODO: Raise 408 (Request Timeout) if this takes too long. - # TODO: Raise 499 (Client Disconnect) if a length is defined and we stop before getting this much data. - size += len(data) - hashmap.append(backend.put_block(data)) - md5.update(data) - - meta['hash'] = md5.hexdigest().lower() - etag = request.META.get('HTTP_ETAG') - if etag and parse_etags(etag)[0].lower() != meta['hash']: - raise UnprocessableEntity('Object ETag does not match') + if request.serialization == 'json': + data = '' + sock = raw_input_socket(request) + for block in socket_read_iterator(sock, content_length, backend.block_size): + data = '%s%s' % (data, block) + d = json.loads(data) + if not hasattr(d, '__getitem__'): + raise BadRequest('Invalid data formating') + try: + hashmap = d['hashes'] + size = d['bytes'] + except KeyError: + raise BadRequest('Invalid data formatting') + meta.update({'hash': hashmap_hash(hashmap)}) # Update ETag. + elif request.rerialization == 'xml': + #TODO support for xml + raise BadRequest('Format xml is not supported') + else: + md5 = hashlib.md5() + size = 0 + hashmap = [] + sock = raw_input_socket(request) + for data in socket_read_iterator(sock, content_length, backend.block_size): + # TODO: Raise 408 (Request Timeout) if this takes too long. + # TODO: Raise 499 (Client Disconnect) if a length is defined and we stop before getting this much data. + size += len(data) + hashmap.append(backend.put_block(data)) + md5.update(data) + + meta['hash'] = md5.hexdigest().lower() + etag = request.META.get('HTTP_ETAG') + if etag and parse_etags(etag)[0].lower() != meta['hash']: + raise UnprocessableEntity('Object ETag does not match') + payload = '' + code = 201 try: backend.update_object_hashmap(request.user, v_account, v_container, v_object, size, hashmap, meta, True, permissions) except NotAllowedError: raise Unauthorized('Access denied') + except IndexError, e: + payload = json.dumps(e.data) + code = 409 except NameError: raise ItemNotFound('Container does not exist') except ValueError: @@ -611,7 +633,7 @@ def object_write(request, v_account, v_container, v_object): except AttributeError: raise Conflict('Sharing already set above or below this path in the hierarchy') - response = HttpResponse(status=201) + response = HttpResponse(content=payload, status=code) response['ETag'] = meta['hash'] return response diff --git a/pithos/backends/simple.py b/pithos/backends/simple.py index 337f70f..00aad0e 100644 --- a/pithos/backends/simple.py +++ b/pithos/backends/simple.py @@ -291,6 +291,16 @@ class SimpleBackend(BaseBackend): if permissions is not None and user != account: raise NotAllowedError self._can_write(user, account, container, name) + missing = [] + for i in range(len(hashmap)): + sql = 'select count(*) from blocks where block_id = ?' + c = self.con.execute(sql, (hashmap[i],)) + if c.fetchone()[0] == 0: + missing.append(hashmap[i]) + if missing: + ie = IndexError() + ie.data = missing + raise ie path = self._get_containerinfo(account, container)[0] path = os.path.join(path, name) if permissions is not None: -- 1.7.10.4