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