* Empty object update.
* Parameter version equal to 'list' in requests.
* Object HEAD response code (should be 200).
* Remove source version parameter when moving with PUT/MOVE.
* Mark 'X-Object-Public' as TBD in the docs.
x_object_version The object's version identifier
x_object_version_timestamp The object's version timestamp
x_object_manifest Object parts prefix in ``<container>/<object>`` form (optional)
-x_object_public Object is publicly accessible (optional)
+x_object_public Object is publicly accessible (optional) (**TBD**)
x_object_meta_* Optional user defined metadata
========================== ======================================
X-Object-Version The object's version identifier
X-Object-Version-Timestamp The object's version timestamp
X-Object-Manifest Object parts prefix in ``<container>/<object>`` form (optional)
-X-Object-Public Object is publicly accessible (optional)
+X-Object-Public Object is publicly accessible (optional) (**TBD**)
X-Object-Meta-* Optional user defined metadata
========================== ===============================
================ ===============================
Return Code Description
================ ===============================
-204 (No Content) The request succeeded
+200 (No Content) The request succeeded
================ ===============================
X-Object-Version The object's version identifier
X-Object-Version-Timestamp The object's version timestamp
X-Object-Manifest Object parts prefix in ``<container>/<object>`` form (optional)
-X-Object-Public Object is publicly accessible (optional)
+X-Object-Public Object is publicly accessible (optional) (**TBD**)
X-Object-Meta-* Optional user defined metadata
========================== ===============================
Transfer-Encoding Set to ``chunked`` to specify incremental uploading (if used, ``Content-Length`` is ignored)
X-Copy-From The source path in the form ``/<container>/<object>``
X-Move-From The source path in the form ``/<container>/<object>``
-X-Source-Version The source version to copy/move from
+X-Source-Version The source version to copy from
Content-Encoding The encoding of the object (optional)
Content-Disposition The presentation style of the object (optional)
X-Object-Manifest Object parts prefix in ``<container>/<object>`` form (optional)
-X-Object-Public Object is publicly accessible (optional)
+X-Object-Public Object is publicly accessible (optional) (**TBD**)
X-Object-Meta-* Optional user defined metadata
==================== ================================
Content-Type The MIME content type of the object (optional)
Content-Encoding The encoding of the object (optional)
Content-Disposition The presentation style of the object (optional)
-X-Source-Version The source version to copy/move from
+X-Source-Version The source version to copy from
X-Object-Manifest Object parts prefix in ``<container>/<object>`` form (optional)
-X-Object-Public Object is publicly accessible (optional)
+X-Object-Public Object is publicly accessible (optional) (**TBD**)
X-Object-Meta-* Optional user defined metadata
==================== ================================
MOVE
""""
-Same as ``COPY``.
+Same as ``COPY``, without the ``X-Source-Version`` request header. The ``MOVE`` operation is always applied on the latest version.
POST
Content-Encoding The encoding of the object (optional)
Content-Disposition The presentation style of the object (optional)
X-Object-Manifest Object parts prefix in ``<container>/<object>`` form (optional)
-X-Object-Public Object is publicly accessible (optional)
+X-Object-Public Object is publicly accessible (optional) (**TBD**)
X-Object-Meta-* Optional user defined metadata
==================== ================================
-The ``Content-Encoding``, ``Content-Disposition``, ``X-Object-Manifest``, ``X-Object-Public`` and ``X-Object-Meta-*`` headers are considered to be user defined metadata. The update operation will overwrite all previous values and remove any keys not supplied.
+The ``Content-Encoding``, ``Content-Disposition``, ``X-Object-Manifest``, ``X-Object-Public`` (**TBD**) and ``X-Object-Meta-*`` headers are considered to be user defined metadata. The update operation will overwrite all previous values and remove any keys not supplied.
To update an object:
202 (Accepted) The request has been accepted (not a data update)
204 (No Content) The request succeeded (data updated)
411 (Length Required) Missing ``Content-Length`` in the request
-416 (Range Not Satisfiable) The supplied range is out of limits or invalid size (**TBD**)
+416 (Range Not Satisfiable) The supplied range is out of limits or invalid size
=========================== ==============================
Public Objects
^^^^^^^^^^^^^^
-Objects that are marked as public, via the ``X-Object-Public`` meta, are also available at the corresponding URI ``https://hostname/public/<account>/<container>/<object>`` for ``HEAD`` or ``GET``. Requests for public objects do not need to include an ``X-Auth-Token``. Pithos will ignore request parameters and only include the following headers in the reply (all ``X-Object-*`` meta is hidden).
+Objects that are marked as public, via the ``X-Object-Public`` meta (**TBD**), are also available at the corresponding URI ``https://hostname/public/<account>/<container>/<object>`` for ``HEAD`` or ``GET``. Requests for public objects do not need to include an ``X-Auth-Token``. Pithos will ignore request parameters and only include the following headers in the reply (all ``X-Object-*`` meta is hidden).
========================== ===============================
Reply Header Name Value
* All metadata replies, at all levels, include latest modification information.
* At all levels, a ``GET`` request may use ``If-Modified-Since`` and ``If-Unmodified-Since`` headers.
* Container/object lists include all associated metadata if the reply is of type json/xml. Some names are kept to their OOS API equivalents for compatibility.
-* Object metadata allowed, in addition to ``X-Object-Meta-*``: ``Content-Encoding``, ``Content-Disposition``, ``X-Object-Manifest``, ``X-Object-Public``. These are all replaced with every update operation.
+* Object metadata allowed, in addition to ``X-Object-Meta-*``: ``Content-Encoding``, ``Content-Disposition``, ``X-Object-Manifest``, ``X-Object-Public`` (**TBD**). These are all replaced with every update operation.
* Multi-range object GET support as outlined in RFC2616.
* Object hashmap retrieval through GET and the ``format`` parameter.
* Partial object updates through POST, using the ``Content-Length``, ``Content-Type``, ``Content-Range`` and ``Transfer-Encoding`` headers.
* Object ``MOVE`` support.
* Time-variant account/container listings via the ``until`` parameter.
-* Object versions - parameter ``version`` in HEAD/GET (list versions with GET), ``X-Object-Version-*`` meta in replies, ``X-Source-Version`` in PUT/COPY/MOVE.
-* Publicly accessible objects via ``https://hostname/public``. Control with ``X-Object-Public``.
+* Object versions - parameter ``version`` in HEAD/GET (list versions with GET), ``X-Object-Version-*`` meta in replies, ``X-Source-Version`` in PUT/COPY.
+* Publicly accessible objects via ``https://hostname/public``. Control with ``X-Object-Public`` (**TBD**).
* Large object support with ``X-Object-Manifest``.
Clarifications/suggestions:
update_manifest_meta, validate_modification_preconditions, validate_matching_preconditions,
split_container_object_string, copy_or_move_object, get_int_parameter, get_content_length,
get_content_range, raw_input_socket, socket_read_iterator, object_data_response,
- hashmap_hash, api_method)
+ put_object_block, hashmap_hash, api_method)
from pithos.backends import backend
update_manifest_meta(request, v_account, meta)
- response = HttpResponse(status=204)
+ response = HttpResponse(status=200)
put_object_meta(response, meta)
return response
# notModified (304)
version = get_int_parameter(request, 'version')
- if not version:
- version = request.GET.get('version')
+ version_list = False
+ if version is None and request.GET.get('version') == 'list':
+ version_list = True
try:
meta = backend.get_object_meta(request.user, v_account, v_container, v_object, version)
except NameError:
return response
# Reply with the version list.
- if version == 'list':
+ if version_list:
if request.serialization == 'text':
raise BadRequest('No format specified for version list.')
offset, length, total = ranges
if offset is None:
offset = size
+ elif offset > size:
+ raise RangeNotSatisfiable('Supplied offset is beyond object limits')
if length is None or content_length == -1:
length = content_length # Nevermind the error.
elif length != content_length:
# 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.
data += d
- bi = int(offset / backend.block_size)
- bo = offset % backend.block_size
- bl = min(len(data), backend.block_size - bo)
- offset += bl
- h = backend.update_block(hashmap[bi], data[:bl], bo)
- if bi < len(hashmap):
- hashmap[bi] = h
- else:
- hashmap.append(h)
- data = data[bl:]
+ bytes = put_object_block(hashmap, data, offset)
+ offset += bytes
+ data = data[bytes:]
if len(data) > 0:
- bi = int(offset / backend.block_size)
- offset += len(data)
- h = backend.update_block(hashmap[bi], data)
- if bi < len(hashmap):
- hashmap[bi] = h
- else:
- hashmap.append(h)
+ put_object_block(hashmap, data, offset)
if offset > size:
size = offset
if not public:
response['X-Object-Version'] = meta['version']
response['X-Object-Version-Timestamp'] = meta['version_timestamp']
- response['X-Object-Size'] = meta['bytes']
for k in [x for x in meta.keys() if x.startswith('X-Object-Meta-')]:
response[k.encode('utf-8')] = meta[k].encode('utf-8')
for k in ('Content-Encoding', 'Content-Disposition', 'X-Object-Manifest', 'X-Object-Public'):
if k in src_meta:
meta[k] = src_meta[k]
- src_version = request.META.get('HTTP_X_SOURCE_VERSION')
try:
if move:
- backend.move_object(request.user, v_account, src_container, src_name, dest_container, dest_name, meta, True, src_version)
+ backend.move_object(request.user, v_account, src_container, src_name, dest_container, dest_name, meta, True)
else:
+ src_version = request.META.get('HTTP_X_SOURCE_VERSION')
backend.copy_object(request.user, v_account, src_container, src_name, dest_container, dest_name, meta, True, src_version)
except NameError:
raise ItemNotFound('Container or object does not exist')
response['Content-Type'] = 'multipart/byteranges; boundary=%s' % (boundary,)
return response
+def put_object_block(hashmap, data, offset):
+ """Put one block of data at the given offset."""
+
+ bi = int(offset / backend.block_size)
+ bo = offset % backend.block_size
+ bl = min(len(data), backend.block_size - bo)
+ if bi < len(hashmap):
+ hashmap[bi] = backend.update_block(hashmap[bi], data[:bl], bo)
+ else:
+ hashmap.append(backend.put_block(('\x00' * bo) + data[:bl]))
+ return bl # Return ammount of data written.
+
def hashmap_hash(hashmap):
"""Produce the root hash, treating the hashmap as a Merkle-like tree."""
"""
return
- def move_object(self, user, account, src_container, src_name, dest_container, dest_name, dest_meta={}, replace_meta=False, src_version=None):
+ def move_object(self, user, account, src_container, src_name, dest_container, dest_name, dest_meta={}, replace_meta=False):
"""Move an object's data and metadata.
Parameters:
'dest_meta': Dictionary with metadata to changes from source to destination
'replace_meta': Replace metadata instead of update
- 'src_version': Copy from the version provided.
Raises:
NameError: Container/object does not exist
- IndexError: Version does not exist
"""
return
self.con.execute(sql, (dest_version_id, k, v))
self.con.commit()
- def move_object(self, user, account, src_container, src_name, dest_container, dest_name, dest_meta={}, replace_meta=False, src_version=None):
+ def move_object(self, user, account, src_container, src_name, dest_container, dest_name, dest_meta={}, replace_meta=False):
"""Move an object's data and metadata."""
- logger.debug("move_object: %s %s %s %s, %s %s %s %s", account, src_container, src_name, dest_container, dest_name, dest_meta, replace_meta, src_version)
- self.copy_object(user, account, src_container, src_name, dest_container, dest_name, dest_meta, replace_meta, src_version)
+ logger.debug("move_object: %s %s %s %s %s %s %s", account, src_container, src_name, dest_container, dest_name, dest_meta, replace_meta)
+ self.copy_object(user, account, src_container, src_name, dest_container, dest_name, dest_meta, replace_meta, None)
self.delete_object(user, account, src_container, src_name)
def delete_object(self, user, account, container, name):
return int(row[0]), int(row[1]), int(tstamp)
def _get_version(self, path, version=None):
- if version in [None, 'list']:
+ if version is None:
sql = '''select version_id, strftime('%s', tstamp), size, hide from versions where name = ?
order by version_id desc limit 1'''
c = self.con.execute(sql, (path,))