Revision 1495b972
b/docs/source/devguide.rst | ||
---|---|---|
25 | 25 |
========================= ================================ |
26 | 26 |
Revision Description |
27 | 27 |
========================= ================================ |
28 |
0.5 (July 11, 2011) Object update from another object's data. |
|
28 |
0.5 (July 12, 2011) Object update from another object's data. |
|
29 |
\ Support object truncate. |
|
29 | 30 |
0.4 (July 01, 2011) Object permissions and account groups. |
30 | 31 |
\ Control versioning behavior and container quotas with container policy directives. |
31 | 32 |
\ Support updating/deleting individual metadata with ``POST``. |
... | ... | |
679 | 680 |
Content-Disposition The presentation style of the object (optional) |
680 | 681 |
X-Source-Object Update with data from the object at path ``/<container>/<object>`` (optional, to update) |
681 | 682 |
X-Source-Version The source version to update from (optional, to update) |
683 |
X-Object-Bytes The updated object's final size (optional, when updating) |
|
682 | 684 |
X-Object-Manifest Object parts prefix in ``<container>/<object>`` form (optional) |
683 | 685 |
X-Object-Sharing Object permissions (optional) |
684 | 686 |
X-Object-Public Object is publicly accessible (optional) |
... | ... | |
699 | 701 |
* Client software SHOULD not specify the ``instance-length`` (use a ``*``), unless there is a reason for performing a size check at the server. |
700 | 702 |
* If ``Content-Range`` used has a ``byte-range-resp-spec = *``, data will be appended to the object. |
701 | 703 |
|
704 |
Optionally, truncate the updated object to the desired length with the ``X-Object-Bytes`` header. |
|
705 |
|
|
702 | 706 |
A data update will trigger an ETag change. The new ETag will not correspond to the object's MD5 sum (**TBD**) and will be included in reply headers. |
703 | 707 |
|
704 | 708 |
No reply content. No reply headers if only metadata is updated. |
... | ... | |
770 | 774 |
* Object metadata allowed, in addition to ``X-Object-Meta-*``: ``Content-Encoding``, ``Content-Disposition``, ``X-Object-Manifest``. These are all replaced with every update operation, except if using the ``update`` parameter (in which case individual keys can also be deleted). Deleting meta by providing empty values also works when copying/moving an object. |
771 | 775 |
* Multi-range object GET support as outlined in RFC2616. |
772 | 776 |
* Object hashmap retrieval through GET and the ``format`` parameter. |
773 |
* Partial object updates through POST, using the ``Content-Length``, ``Content-Type``, ``Content-Range`` and ``Transfer-Encoding`` headers. Use another object's data to update with ``X-Source-Object`` and ``X-Source-Version``. |
|
777 |
* Partial object updates through POST, using the ``Content-Length``, ``Content-Type``, ``Content-Range`` and ``Transfer-Encoding`` headers. Use another object's data to update with ``X-Source-Object`` and ``X-Source-Version``. Truncate with ``X-Object-Bytes``.
|
|
774 | 778 |
* Object ``MOVE`` support. |
775 | 779 |
* Time-variant account/container listings via the ``until`` parameter. |
776 | 780 |
* Object versions - parameter ``version`` in HEAD/GET (list versions with GET), ``X-Object-Version-*`` meta in replies, ``X-Source-Version`` in PUT/COPY. |
... | ... | |
945 | 949 |
-d "0123456789" \ |
946 | 950 |
https://pithos.dev.grnet.gr/v1/user/folder/EXAMPLE.txt |
947 | 951 |
|
952 |
* Update an object (truncate) :: |
|
953 |
|
|
954 |
curl -X POST -D - \ |
|
955 |
-H "X-Auth-Token: 0000" \ |
|
956 |
-H "X-Source-Object: /folder/EXAMPLE.txt" \ |
|
957 |
-H "Content-Range: bytes 0-0/*" \ |
|
958 |
-H "X-Object-Bytes: 0" \ |
|
959 |
https://pithos.dev.grnet.gr/v1/user/folder/EXAMPLE.txt |
|
960 |
|
|
961 |
This will truncate the object to 0 bytes. |
|
962 |
|
|
948 | 963 |
* Add object metadata :: |
949 | 964 |
|
950 | 965 |
curl -X POST -D - \ |
b/pithos/api/functions.py | ||
---|---|---|
128 | 128 |
# unauthorized (401), |
129 | 129 |
# badRequest (400) |
130 | 130 |
|
131 |
until = get_int_parameter(request, 'until')
|
|
131 |
until = get_int_parameter(request.GET.get('until'))
|
|
132 | 132 |
try: |
133 | 133 |
meta = backend.get_account_meta(request.user, v_account, until) |
134 | 134 |
groups = backend.get_account_groups(request.user, v_account) |
... | ... | |
171 | 171 |
# unauthorized (401), |
172 | 172 |
# badRequest (400) |
173 | 173 |
|
174 |
until = get_int_parameter(request, 'until')
|
|
174 |
until = get_int_parameter(request.GET.get('until'))
|
|
175 | 175 |
try: |
176 | 176 |
meta = backend.get_account_meta(request.user, v_account, until) |
177 | 177 |
groups = backend.get_account_groups(request.user, v_account) |
... | ... | |
239 | 239 |
# unauthorized (401), |
240 | 240 |
# badRequest (400) |
241 | 241 |
|
242 |
until = get_int_parameter(request, 'until')
|
|
242 |
until = get_int_parameter(request.GET.get('until'))
|
|
243 | 243 |
try: |
244 | 244 |
meta = backend.get_container_meta(request.user, v_account, v_container, until) |
245 | 245 |
meta['object_meta'] = backend.list_object_meta(request.user, v_account, v_container, until) |
... | ... | |
337 | 337 |
# unauthorized (401), |
338 | 338 |
# badRequest (400) |
339 | 339 |
|
340 |
until = get_int_parameter(request, 'until')
|
|
340 |
until = get_int_parameter(request.GET.get('until'))
|
|
341 | 341 |
try: |
342 | 342 |
meta = backend.get_container_meta(request.user, v_account, v_container, until) |
343 | 343 |
meta['object_meta'] = backend.list_object_meta(request.user, v_account, v_container, until) |
... | ... | |
827 | 827 |
if total is not None and (total != size or offset >= size or (length > 0 and offset + length >= size)): |
828 | 828 |
raise RangeNotSatisfiable('Supplied range will change provided object limits') |
829 | 829 |
|
830 |
dest_bytes = request.META.get('HTTP_X_OBJECT_BYTES') |
|
831 |
if dest_bytes is not None: |
|
832 |
dest_bytes = get_int_parameter(dest_bytes) |
|
833 |
if dest_bytes is None: |
|
834 |
raise BadRequest('Invalid X-Object-Bytes header') |
|
835 |
|
|
830 | 836 |
if src_object: |
831 | 837 |
if offset % backend.block_size == 0: |
832 | 838 |
# Update the hashes only. |
... | ... | |
872 | 878 |
|
873 | 879 |
if offset > size: |
874 | 880 |
size = offset |
881 |
if dest_bytes is not None and dest_bytes < size: |
|
882 |
size = dest_bytes |
|
883 |
hashmap = hashmap[:(int((size - 1) / backend.block_size) + 1)] |
|
875 | 884 |
meta.update({'hash': hashmap_hash(hashmap)}) # Update ETag. |
876 | 885 |
try: |
877 | 886 |
backend.update_object_hashmap(request.user, v_account, v_container, v_object, size, hashmap, meta, replace, permissions) |
b/pithos/api/util.py | ||
---|---|---|
269 | 269 |
except NameError: |
270 | 270 |
raise ItemNotFound('Object does not exist') |
271 | 271 |
|
272 |
def get_int_parameter(request, name): |
|
273 |
p = request.GET.get(name) |
|
272 |
def get_int_parameter(p): |
|
274 | 273 |
if p is not None: |
275 | 274 |
try: |
276 | 275 |
p = int(p) |
... | ... | |
281 | 280 |
return p |
282 | 281 |
|
283 | 282 |
def get_content_length(request): |
284 |
content_length = request.META.get('CONTENT_LENGTH') |
|
285 |
if not content_length: |
|
286 |
raise LengthRequired('Missing Content-Length header') |
|
287 |
try: |
|
288 |
content_length = int(content_length) |
|
289 |
if content_length < 0: |
|
290 |
raise ValueError |
|
291 |
except ValueError: |
|
292 |
raise BadRequest('Invalid Content-Length header') |
|
283 |
content_length = get_int_parameter(request.META.get('CONTENT_LENGTH')) |
|
284 |
if content_length is None: |
|
285 |
raise LengthRequired('Missing or invalid Content-Length header') |
|
293 | 286 |
return content_length |
294 | 287 |
|
295 | 288 |
def get_range(request, size): |
Also available in: Unified diff