Fix precondition check replies.
[pithos] / pithos / api / util.py
index 0c60b3a..f36ad3c 100644 (file)
@@ -35,6 +35,7 @@ from functools import wraps
 from time import time
 from traceback import format_exc
 from wsgiref.handlers import format_date_time
+from binascii import hexlify
 
 from django.conf import settings
 from django.http import HttpResponse
@@ -48,6 +49,7 @@ from pithos.backends import backend
 import datetime
 import logging
 import re
+import hashlib
 
 
 logger = logging.getLogger(__name__)
@@ -101,6 +103,8 @@ def put_container_meta(response, meta):
     for k in [x for x in meta.keys() if x.startswith('X-Container-Meta-')]:
         response[k.encode('utf-8')] = meta[k].encode('utf-8')
     response['X-Container-Object-Meta'] = [x[14:] for x in meta['object_meta'] if x.startswith('X-Object-Meta-')]
+    response['X-Container-Block-Size'] = backend.block_size
+    response['X-Container-Block-Hash'] = backend.hash_algorithm
 
 def get_object_meta(request):
     """Get metadata from an object request"""
@@ -136,13 +140,13 @@ def validate_modification_preconditions(request, meta):
     if if_modified_since is not None:
         if_modified_since = parse_http_date_safe(if_modified_since)
     if if_modified_since is not None and int(meta['modified']) <= if_modified_since:
-        raise NotModified('Object has not been modified')
+        raise NotModified('Resource has not been modified')
     
     if_unmodified_since = request.META.get('HTTP_IF_UNMODIFIED_SINCE')
     if if_unmodified_since is not None:
         if_unmodified_since = parse_http_date_safe(if_unmodified_since)
     if if_unmodified_since is not None and int(meta['modified']) > if_unmodified_since:
-        raise PreconditionFailed('Object has been modified')
+        raise PreconditionFailed('Resource has been modified')
 
 def validate_matching_preconditions(request, meta):
     """Check that the ETag conforms with the preconditions set"""
@@ -152,12 +156,12 @@ def validate_matching_preconditions(request, meta):
     if_match = request.META.get('HTTP_IF_MATCH')
     if if_match is not None and if_match != '*':
         if meta['hash'] not in [x.lower() for x in parse_etags(if_match)]:
-            raise PreconditionFailed('Object Etag does not match')
+            raise PreconditionFailed('Resource Etag does not match')
     
     if_none_match = request.META.get('HTTP_IF_NONE_MATCH')
     if if_none_match is not None:
         if if_none_match == '*' or meta['hash'] in [x.lower() for x in parse_etags(if_none_match)]:
-            raise NotModified('Object Etag matches')
+            raise NotModified('Resource Etag matches')
 
 def copy_or_move_object(request, src_path, dest_path, move=False):
     """Copy or move an object"""
@@ -275,12 +279,12 @@ def get_content_range(request):
         total = int(total)
     else:
         total = None
-    if (upto and offset > upto) or \
-        (total and offset >= total) or \
-        (total and upto and upto >= total):
+    if (upto is not None and offset > upto) or \
+        (total is not None and offset >= total) or \
+        (total is not None and upto is not None and upto >= total):
         return None
     
-    if not upto:
+    if upto is None:
         length = None
     else:
         length = upto - offset + 1
@@ -425,6 +429,27 @@ class ObjectWrapper(object):
                 out.append('')
                 return '\r\n'.join(out)
 
+def hashmap_hash(hashmap):
+    """ Produce the root hash, treating the hashmap as a Merkle-like tree."""
+    
+    def subhash(d):
+        h = hashlib.new(backend.hash_algorithm)
+        h.update(d)
+        return h.digest()
+    
+    if len(hashmap) == 0:
+        return hexlify(subhash(''))
+    if len(hashmap) == 1:
+        return hexlify(subhash(hashmap[0]))
+    s = 2
+    while s < len(hashmap):
+        s = s * 2
+    h = hashmap + ([('\x00' * len(hashmap[0]))] * (s - len(hashmap)))
+    h = [subhash(h[x] + (h[x + 1] if x + 1 < len(h) else '')) for x in range(0, len(h), 2)]
+    while len(h) > 1:
+        h = [subhash(h[x] + (h[x + 1] if x + 1 < len(h) else '')) for x in range(0, len(h), 2)]
+    return hexlify(h[0])
+
 def update_response_headers(request, response):
     if request.serialization == 'xml':
         response['Content-Type'] = 'application/xml; charset=UTF-8'