Implement kamakicon, use it
authorStavros Sachtouris <saxtouri@admin.grnet.gr>
Tue, 11 Sep 2012 15:10:34 +0000 (18:10 +0300)
committerStavros Sachtouris <saxtouri@admin.grnet.gr>
Tue, 11 Sep 2012 15:10:34 +0000 (18:10 +0300)
kamakicon: an httplib/ObjectPool based connection class

kamaki/clients/__init__.py
kamaki/clients/connection/__init__.py
kamaki/clients/connection/kamakicon.py
kamaki/clients/connection/pool/http.py
kamaki/clients/connection/request.py
kamaki/clients/pithos.py
kamaki/clients/storage.py
kamaki/clients/tests.py

index 29d6648..0a7f836 100644 (file)
@@ -34,8 +34,8 @@
 import json
 import logging
 from .connection import HTTPConnectionError
-from .connection.request import HTTPRequest
-#from .connection.kamakicon import KamakiHTTPConnection
+#from .connection.request import HTTPRequest
+from .connection.kamakicon import KamakiHTTPConnection
 
 sendlog = logging.getLogger('clients.send')
 recvlog = logging.getLogger('clients.recv')
@@ -50,7 +50,7 @@ class ClientError(Exception):
 
 class Client(object):
 
-    def __init__(self, base_url, token, http_client=HTTPRequest()):
+    def __init__(self, base_url, token, http_client=KamakiHTTPConnection()):
         self.base_url = base_url
         self.token = token
         self.headers = {}
@@ -98,11 +98,11 @@ class Client(object):
 
             #kwargs.setdefault('verify', False)  # Disable certificate verification
             self.http_client.url = self.base_url + path
-            r = self.http_client.perform_request(method=method, data=data, binary=binary)
+            r = self.http_client.perform_request(method=method, data=data)
             #r = requests.request(method, url, headers=self.headers, data=data, **kwargs)
 
             req = self.http_client
-            sendlog.info('%s %s', req.method, req.url)
+            sendlog.info('%s %s', method, req.url)
             for key, val in req.headers.items():
                 sendlog.info('%s: %s', key, val)
             sendlog.info('')
index b95f177..e5542b6 100644 (file)
 # interpreted as representing official policies, either expressed
 # or implied, of GRNET S.A.
 
-from .pool import ObjectPool
-
-POOL_SIZE=8
-
-class HTTPResponsePool(ObjectPool):
-
-    def __init__(self, netloc, size=POOL_SIZE):
-        super(HTTPResponsePool, self).__init__(size=size)
-        self.netloc = netloc
-
-    def _pool_create(self):
-        resp = HTTPResponse()
-        resp._pool = self
-        return resp
-
-    def _pool_cleanup(self, resp):
-        resp._get_response()
-        return True
-
 class HTTPResponse(object):
 
     def __init__(self, request=None, prefetched=False):
         self.request=request
-        if prefetched:
-            self = request.response
         self.prefetched = prefetched
 
     def _get_response(self):
index 21626e8..4eb0ced 100644 (file)
@@ -35,6 +35,7 @@ from urlparse import urlparse
 from .pool.http import get_http_connection
 from . import HTTPConnection, HTTPResponse, HTTPConnectionError
 
+from json import loads
 
 from time import sleep
 from httplib import ResponseNotReady
@@ -42,25 +43,30 @@ from httplib import ResponseNotReady
 class KamakiHTTPResponse(HTTPResponse):
 
     def _get_response(self):
-        print('KamakiHTTPResponse:should I get response?')
         if self.prefetched:
-            print('\tKamakiHTTPResponse: no, I have already done that before')
             return
-        print('\tKamakiHTTPResponse: yes, pls')
-        r = self.request.getresponse()
+
+        ready = False
+        while not ready:
+            try:
+                r = self.request.getresponse()
+            except ResponseNotReady:
+                sleep(0.2)
+                continue
+            break
         self.prefetched = True
         headers = {}
         for k,v in r.getheaders():
             headers.update({k:v})
         self.headers = headers
-        self.content = r.read(r.length)
+        self.content = r.read()
         self.status_code = r.status
         self.status = r.reason
-        print('KamakiHTTPResponse: Niiiiice')
+        self.request.close()
 
     @property 
     def text(self):
-        _get_response()
+        self._get_response()
         return self._content
     @text.setter
     def test(self, v):
@@ -68,8 +74,7 @@ class KamakiHTTPResponse(HTTPResponse):
 
     @property 
     def json(self):
-        _get_response()
-        from json import loads
+        self._get_response()
         try:
             return loads(self._content)
         except ValueError as err:
@@ -78,65 +83,32 @@ class KamakiHTTPResponse(HTTPResponse):
     def json(self, v):
         pass
 
-class KamakiHTTPConnection(HTTPConnection):
-
-    url         =   None
-    scheme      =   None
-    netloc      =   None
-    method      =   None
-    data        =   None
-    headers     =   None
+    def release(self):
+        if not self.prefetched:
+            self.request.close()
 
-    scheme_ports = {
-            'http':     '80',
-            'https':    '443',
-    }
 
-    def _load_connection_settings(self, url=None, scheme=None, params=None, headers=None, host=None,
-        port=None, method=None):
-        if params is not None:
-            self.params = params
-        if headers is not None:
-            self.headers = headers
-
-        if url is None:
-            url = self.url
-        if host is None or scheme is None:
-            p = urlparse(url)
-            netloc = p.netloc
-            if not netloc:
-                netloc = 'localhost'
-            scheme = p.scheme
-            if not scheme:
-                scheme = 'http'
-            param_str = ''
-            for i,(key, val) in enumerate(self.params.items()):
-                param_str = ('?' if i == 0 else '&') + unicode(key) 
-                if val is not None:
-                    param_str+= '='+unicode(val)
-            url = p.path + param_str
-        else:
-            host = host
-            port = port if port is not None else self.scheme_ports[scheme]
-            #NOTE: we force host:port as canonical form,
-            #      lest we have a cache miss 'host' vs 'host:80'
-            netloc = "%s%s" % (host, port)
+class KamakiHTTPConnection(HTTPConnection):
 
-        self.netloc = netloc
-        self.url = url #if url in (None, '') or url[0] != '/' else url[1:]
-        self.scheme = scheme
+    def _retrieve_connection_info(self):
+        """ return (scheme, netloc, url?with&params) """
+        url = self.url
+        for i,(key, val) in enumerate(self.params.items()):
+            param_str = ('?' if i == 0 else '&') + unicode(key) 
+            if val is not None:
+                param_str+= '='+unicode(val)
+            url += param_str
 
-        if method is not None:
-            self.method = method
+        parsed = urlparse(self.url)
+        self.url = url
+        return (parsed.scheme, parsed.netloc)
 
-    def perform_request(self, url=None, params=None, headers=None, method=None, host=None,
-        port=None, data=None):
-        self._load_connection_settings(url=url, params=params, headers=headers, host=host,
-            port=port, method=method)
-        print('---> %s %s %s %s %s'%(self.method, self.scheme, self.netloc, self.url, self.headers))
-        conn = get_http_connection(netloc=self.netloc, scheme=self.scheme)
+    def perform_request(self, method=None, data=None):
+        (scheme, netloc) = self._retrieve_connection_info()
+        #get connection from pool
+        conn = get_http_connection(netloc=netloc, scheme=scheme)
         try:
-            conn.request(self.method, self.url, headers=self.headers, body=data)
+            conn.request(method = method.upper(), url=self.url, headers=self.headers, body=data)
         except:
             conn.close()
             raise
index 2992956..0e1bea9 100644 (file)
@@ -115,7 +115,13 @@ def get_http_connection(netloc=None, scheme='http'):
 
 def main():
     #cpool = HTTPConnectionPool('https', 'pithos.okeanos.io/v1', size=8)
+    headers={'X-Auth-Token':'0TpoyAXqJSPxLdDuZHiLOA=='}
     c = get_http_connection('pithos.okeanos.io', 'https')
+    c.request(method='get', url='https://pithos.okeanos.io/v1/saxtouri@admin.grnet.gr?format=json',
+        headers=headers)
+    r = c.getresponse()
+    print('HEADERS:'+unicode(r.getheaders()))
+    print('BODY:'+unicode(r.read()))
 
 
 if __name__ == '__main__':
index 2b9e713..f1aab23 100644 (file)
@@ -32,7 +32,7 @@
 # or implied, of GRNET S.A.
 
 import requests
-from . import HTTPConnection, HTTPResponse, HTTPConnectionError, HTTPResponsePool
+from . import HTTPConnection, HTTPResponse, HTTPConnectionError
 from .pool import ObjectPool
 from urlparse import urlparse
 
@@ -43,7 +43,10 @@ requests.Response.status = property(_status)
 
 class HTTPRequestsResponse(HTTPResponse):
 
-       _get_content_only=False
+    def __init__(self, request=None, prefetched=False):
+       super(HTTPRequestsResponse, self).__init__(request=request, prefetched=prefetched)
+        if prefetched:
+            self = request.response
 
        def _get_response(self):
                if self.prefetched:
@@ -54,8 +57,12 @@ class HTTPRequestsResponse(HTTPResponse):
                        self.status = r.status
                        self.status_code = r.status_code
                        self.content = r.content if hasattr(r, 'content') else None
-                       self.text = None if self._get_content_only else r.text
-                       self.json = None if self._get_content_only else r.json
+                       from json import loads
+                       try:
+                               self.json = loads(r.content)#None if self._get_content_only else r.json
+                       except ValueError:
+                               self.json = None
+                       self.text = r.content#None if self._get_content_only else r.text
                        self.exception = r.exception if hasattr(r, 'exception') else None
                except requests.ConnectionError as err:
                        raise HTTPConnectionError('Connection error', status=651, details=err.message)
@@ -74,7 +81,15 @@ class HTTPRequestsResponse(HTTPResponse):
                if hasattr(self, '_pool'):
                        self._pool.pool_put(self)
 
-class HTTPRequestsResponsePool(HTTPResponsePool):
+POOL_SIZE=8
+class HTTPRequestsResponsePool(ObjectPool):
+    def __init__(self, netloc, size=POOL_SIZE):
+        super(ObjectPool, self).__init__(size=size)
+        self.netloc = netloc
+
+    def _pool_cleanup(self, resp):
+        resp._get_response()
+        return True
 
        @classmethod
        def key(self, full_url):
@@ -102,8 +117,7 @@ class HTTPRequest(HTTPConnection):
                        respool = self._pools[pool_key]
                return respool.pool_get()
 
-       def perform_request(self, method=None, url=None, params=None, headers=None, data=None,
-               binary=False):
+       def perform_request(self, method=None, url=None, params=None, headers=None, data=None):
                """perform a request
                Example: method='PUT' url='https://my.server:8080/path/to/service'
                        params={'update':None, 'format':'json'} headers={'X-Auth-Token':'s0m3t0k3n=='}
@@ -130,6 +144,4 @@ class HTTPRequest(HTTPConnection):
                self._response_object = requests.request(self.method, self.url, headers=self.headers, data=data,
                        verify=self.verify, prefetch = False)
                res.request = self._response_object.request
-               if binary:
-                       res._get_content_only = True
                return res
index 97e46df..37d85f8 100644 (file)
@@ -39,6 +39,7 @@ gevent.monkey.patch_all()
 import hashlib, os, gevent.pool
 
 from time import time
+from datetime import datetime
 
 from .storage import StorageClient, ClientError
 from .utils import path4url, prefix_keys, filter_in, filter_out, list2str
@@ -604,7 +605,8 @@ class PithosClient(StorageClient):
         return self.delete(path, *args, success=success, **kwargs)
 
     def purge_container(self):
-        self.container_delete(until=unicode(time()))
+        r = self.container_delete(until=unicode(time()))
+        r.release()
 
     def upload_object_unchunked(self, obj, f, withHashFile = False, size=None, etag=None,
         content_encoding=None, content_disposition=None, content_type=None, sharing=None,
@@ -625,9 +627,10 @@ class PithosClient(StorageClient):
             from StringIO import StringIO
             f = StringIO(data)
         data = f.read(size) if size is not None else f.read()
-        self.object_put(obj, data=data, etag=etag, content_encoding=content_encoding,
+        r = self.object_put(obj, data=data, etag=etag, content_encoding=content_encoding,
             content_disposition=content_disposition, content_type=content_type, permitions=sharing,
             public=public, success=201)
+        r.release()
 
     def put_block_async(self, data, hash):
         class SilentGreenlet(gevent.Greenlet):
@@ -655,9 +658,10 @@ class PithosClient(StorageClient):
         content_disposition=None, content_type=None, sharing=None, public=None):
         self.assert_container()
         obj_content_type = 'application/octet-stream' if content_type is None else content_type
-        self.object_put(obj, content_length=0, etag=etag, content_encoding=content_encoding,
+        r = self.object_put(obj, content_length=0, etag=etag, content_encoding=content_encoding,
             content_disposition=content_disposition, content_type=content_type, permitions=sharing,
             public=public, manifest='%s/%s'%(self.container,obj))
+        r.release()
 
     def upload_object(self, object, f, size=None, hash_cb=None, upload_cb=None, etag=None,
         content_encoding=None, content_disposition=None, content_type=None, sharing=None,
@@ -674,7 +678,7 @@ class PithosClient(StorageClient):
         size = size if size is not None else os.fstat(f.fileno()).st_size
         nblocks = 1 + (size - 1) // blocksize
         hashes = []
-        map = {}
+        hmap = {}
 
         offset = 0
 
@@ -687,7 +691,7 @@ class PithosClient(StorageClient):
             bytes = len(block)
             hash = pithos_hash(block, blockhash)
             hashes.append(hash)
-            map[hash] = (offset, bytes)
+            hmap[hash] = (offset, bytes)
             offset += bytes
             if hash_cb:
                 hash_gen.next()
@@ -710,11 +714,11 @@ class PithosClient(StorageClient):
         if upload_cb:
             upload_gen = upload_cb(len(missing))
             upload_gen.next()
+        r .release()
 
         flying = []
-        r .release()
         for hash in missing:
-            offset, bytes = map[hash]
+            offset, bytes = hmap[hash]
             f.seek(offset)
             data = f.read(bytes)
             r = self.put_block_async(data, hash)
@@ -729,8 +733,9 @@ class PithosClient(StorageClient):
             flying = [r for r in flying if not r.ready()]
 
         gevent.joinall(flying)
-        self.object_put(object, format='json', hashmap=True, content_type=obj_content_type, 
+        r = self.object_put(object, format='json', hashmap=True, content_type=obj_content_type, 
             json=hashmap, success=201)
+        r.release()
 
     def download_object(self, obj, f, download_cb=None, version=None, overide=False,
         range=None, if_match=None, if_none_match=None, if_modified_since=None,
@@ -776,8 +781,8 @@ class PithosClient(StorageClient):
             download_gen.next()
 
         #load local file existing hashmap
+        hash_dict = {}
         if islocalfile:
-            hash_dict = {}
             from os import path
             if path.exists(f.name):
                 from binascii import hexlify
@@ -797,6 +802,7 @@ class PithosClient(StorageClient):
                             status=600)
 
         #download and save/print
+        flying = []
         for i, h in enumerate(map):
             #if not islocalfile and h in hash_dict:
             if h in hash_dict:
@@ -813,19 +819,75 @@ class PithosClient(StorageClient):
             if range is not None and end > custom_end:
                 end = custom_end
             data_range = 'bytes=%s-%s'%(start, end)
-            r = self.object_get(obj, data_range=data_range, success=(200, 206), version=version,
-                if_etag_match=if_match, if_etag_not_match=if_none_match, binary=True,
-                if_modified_since=if_modified_since, if_unmodified_since=if_unmodified_since)
+            result_array = []
             if islocalfile:
-                f.seek(start)
-            f.write(r.content)
-            f.flush()
-            r.release()
-            #f.write(data.text.encode('utf-8'))
-
-        if overide and not islocalfile:
+                handler = self._get_block_async(obj, data_range=data_range, version=version,
+                    if_etag_match=if_match, if_etag_not_match=if_none_match,
+                    if_modified_since=if_modified_since, if_unmodified_since=if_unmodified_since)
+                flying.append({'handler':handler, 'start':start})
+                newflying = []
+                for v in flying:
+                    h = v['handler']
+                    if h.ready():
+                        if h.exception:
+                            h.release()
+                            raise h.exception
+                        f.seek(v['start'])
+                        f.write(h.value.content)
+                        f.flush()
+                        h.value.release()
+                    else:
+                        newflying.append(v)
+                flying = newflying
+            else:
+                r = self._get_block(obj, data_range=data_range, version=version,
+                    if_etag_match=if_match, if_etag_not_match=if_none_match,
+                    if_modified_since=if_modified_since, if_unmodified_since=if_unmodified_since)
+                f.write(r.content)
+                f.flush()
+                r.release()
+
+        #write the last results and exit
+        if islocalfile:
+            from time import sleep
+            while len(flying) > 0:
+                result_array=[]
+                newflying = []
+                for v in flying:
+                    h = v['handler']
+                    if h.ready():
+                        if h.exception:
+                            h.release()
+                            raise h.exception
+                        f.seek(v['start'])
+                        f.write(h.value.content)
+                        f.flush()
+                        h.value.release()
+                    else:
+                        sleep(.2)
+                        newflying.append(v)
+                flying = newflying
             f.truncate(total_size)
 
+    def _get_block(self, obj, **kwargs):
+        r = self.object_get(obj, success=(200, 206), binary=True, **kwargs)
+        return r
+
+    def _get_block_async(self, obj, **kwargs):
+        class SilentGreenlet(gevent.Greenlet):
+            def _report_error(self, exc_info):
+                _stderr = sys._stderr
+                try:
+                    sys.stderr = StringIO()
+                    gevent.Greenlet._report_error(self, exc_info)
+                finally:
+                    sys.stderr = _stderr
+        POOL_SIZE = 5
+        if self.async_pool is None:
+            self.async_pool = gevent.pool.Pool(size=POOL_SIZE)
+        g = SilentGreenlet(self._get_block, obj, **kwargs)
+        self.async_pool.start(g)
+        return g
 
     def get_object_hashmap(self, obj, version=None, if_match=None, if_none_match=None,
         if_modified_since=None, if_unmodified_since=None):
@@ -834,6 +896,7 @@ class PithosClient(StorageClient):
                 if_etag_not_match=if_none_match, if_modified_since=if_modified_since,
                 if_unmodified_since=if_unmodified_since)
         except ClientError as err:
+            r.release()
             if err.status == 304 or err.status == 412:
                 return {}
             raise
@@ -842,17 +905,21 @@ class PithosClient(StorageClient):
         return result
 
     def set_account_group(self, group, usernames):
-        self.account_post(update=True, groups = {group:usernames})
+        r = self.account_post(update=True, groups = {group:usernames})
+        r.release()
 
     def del_account_group(self, group):
-        return self.account_post(update=True, groups={group:[]})
+        r = self.account_post(update=True, groups={group:[]})
+        r.release()
 
     def get_account_info(self, until=None):
-        from datetime import datetime
         r = self.account_head(until=until)
         if r.status_code == 401:
+            r.release()
             raise ClientError("No authorization")
-        return r.headers
+        reply = r.headers
+        r.release()
+        return reply
 
     def get_account_quota(self):
         return filter_in(self.get_account_info(), 'X-Account-Policy-Quota', exactMatch = True)
@@ -868,28 +935,37 @@ class PithosClient(StorageClient):
 
     def set_account_meta(self, metapairs):
         assert(type(metapairs) is dict)
-        self.account_post(update=True, metadata=metapairs)
+        r = self.account_post(update=True, metadata=metapairs)
+        r.release()
 
     def del_account_meta(self, metakey):
-        self.account_post(update=True, metadata={metakey:''})
+        r = self.account_post(update=True, metadata={metakey:''})
+        r.release()
 
     def set_account_quota(self, quota):
-        self.account_post(update=True, quota=quota)
+        r = self.account_post(update=True, quota=quota)
+        r.release()
 
     def set_account_versioning(self, versioning):
-        self.account_post(update=True, versioning = versioning)
+        r = self.account_post(update=True, versioning = versioning)
+        r.release()
 
     def list_containers(self):
         r = self.account_get()
-        return r.json
+        reply = r.json
+        r.release()
+        return reply
 
     def del_container(self, until=None, delimiter=None):
         self.assert_container()
         r = self.container_delete(until=until, delimiter=delimiter, success=(204, 404, 409))
         if r.status_code == 404:
+            r.release()
             raise ClientError('Container "%s" does not exist'%self.container, r.status_code)
         elif r.status_code == 409:
+            r.release()
             raise ClientError('Container "%s" is not empty'%self.container, r.status_code)
+        r.release()
 
     def get_container_versioning(self, container):
         self.container = container
@@ -901,6 +977,8 @@ class PithosClient(StorageClient):
 
     def get_container_info(self, until = None):
         r = self.container_head(until=until)
+        reply = r.headers
+        r.release()
         return r.headers
 
     def get_container_meta(self, until = None):
@@ -911,37 +989,48 @@ class PithosClient(StorageClient):
 
     def set_container_meta(self, metapairs):
         assert(type(metapairs) is dict)
-        self.container_post(update=True, metadata=metapairs)
+        r = self.container_post(update=True, metadata=metapairs)
+        r.release()
 
     def del_container_meta(self, metakey):
-        self.container_post(update=True, metadata={metakey:''})
+        r = self.container_post(update=True, metadata={metakey:''})
+        r.release()
 
     def set_container_quota(self, quota):
-        self.container_post(update=True, quota=quota)
+        r = self.container_post(update=True, quota=quota)
+        r.release()
 
     def set_container_versioning(self, versioning):
-        self.container_post(update=True, versioning=versioning)
+        r = self.container_post(update=True, versioning=versioning)
+        r.release()
 
     def del_object(self, obj, until=None, delimiter=None):
         self.assert_container()
-        self.object_delete(obj, until=until, delimiter=delimiter)
+        r = self.object_delete(obj, until=until, delimiter=delimiter)
+        r.release()
 
     def set_object_meta(self, object, metapairs):
         assert(type(metapairs) is dict)
-        self.object_post(object, update=True, metadata=metapairs)
+        r = self.object_post(object, update=True, metadata=metapairs)
+        r.release()
 
     def del_object_meta(self, metakey, object):
-        self.object_post(object, update=True, metadata={metakey:''})
+        r = self.object_post(object, update=True, metadata={metakey:''})
+        r.release()
 
     def publish_object(self, object):
-        self.object_post(object, update=True, public=True)
+        r = self.object_post(object, update=True, public=True)
+        r.release()
 
     def unpublish_object(self, object):
-        self.object_post(object, update=True, public=False)
+        r = self.object_post(object, update=True, public=False)
+        r.release()
 
     def get_object_info(self, obj, version=None):
         r = self.object_head(obj, version=version)
-        return r.headers
+        reply = r.headers
+        r.release()
+        return reply
 
     def get_object_meta(self, obj, version=None):
         return filter_in(self.get_object_info(obj, version=version), 'X-Object-Meta')
@@ -971,7 +1060,8 @@ class PithosClient(StorageClient):
         perms = {}
         perms['read'] = read_permition if isinstance(read_permition, list) else ''
         perms['write'] = write_permition if isinstance(write_permition, list) else ''
-        self.object_post(object, update=True, permitions=perms)
+        r = self.object_post(object, update=True, permitions=perms)
+        r.release()
 
     def del_object_sharing(self, object):
         self.set_object_sharing(object)
@@ -992,15 +1082,17 @@ class PithosClient(StorageClient):
         for i in range(nblocks):
             block = source_file.read(min(blocksize, filesize - offset))
             offset += len(block)
-            self.object_post(object, update=True, content_range='bytes */*',
+            r = self.object_post(object, update=True, content_range='bytes */*',
                 content_type='application/octet-stream', content_length=len(block), data=block)
+            r.release()
             if upload_cb is not None:
                 upload_gen.next()
 
     def truncate_object(self, object, upto_bytes):
-        self.object_post(object, update=True, content_range='bytes 0-%s/*'%upto_bytes,
+        r = self.object_post(object, update=True, content_range='bytes 0-%s/*'%upto_bytes,
             content_type='application/octet-stream', object_bytes=upto_bytes,
             source_object=path4url(self.container, object))
+        r.release()
 
     def overwrite_object(self, object, start, end, source_file, upload_cb=None):
         """Overwrite a part of an object with given source file
@@ -1019,8 +1111,9 @@ class PithosClient(StorageClient):
         for i in range(nblocks):
             block = source_file.read(min(blocksize, filesize - offset, datasize - offset))
             offset += len(block)
-            self.object_post(object, update=True, content_type='application/octet-stream', 
+            r = self.object_post(object, update=True, content_type='application/octet-stream', 
                 content_length=len(block), content_range='bytes %s-%s/*'%(start,end), data=block)
+            r.release()
             if upload_cb is not None:
                 upload_gen.next()
 
@@ -1030,9 +1123,10 @@ class PithosClient(StorageClient):
         self.container = dst_container
         dst_object = dst_object or src_object
         src_path = path4url(src_container, src_object)
-        self.object_put(dst_object, success=201, copy_from=src_path, content_length=0,
+        r = self.object_put(dst_object, success=201, copy_from=src_path, content_length=0,
             source_version=source_version, public=public, content_type=content_type,
             delimiter=delimiter)
+        r.release()
 
     def move_object(self, src_container, src_object, dst_container, dst_object=False,
         source_version = None, public=False, content_type=None, delimiter=None):
@@ -1040,6 +1134,7 @@ class PithosClient(StorageClient):
         self.container = dst_container
         dst_object = dst_object or src_object
         src_path = path4url(src_container, src_object)
-        self.object_put(dst_object, success=201, move_from=src_path, content_length=0,
+        r = self.object_put(dst_object, success=201, move_from=src_path, content_length=0,
             source_version=source_version, public=public, content_type=content_type,
             delimiter=delimiter)
+        r.release()
index 0e44e0f..892dc92 100644 (file)
@@ -33,7 +33,7 @@
 
 from . import Client, ClientError
 from .utils import filter_in, filter_out, prefix_keys, path4url
-from .connection.kamakicon import KamakiHTTPConnection
+#from .connection.kamakicon import KamakiHTTPConnection
 
 class StorageClient(Client):
     """OpenStack Object Storage API 1.0 client"""
@@ -58,15 +58,19 @@ class StorageClient(Client):
         path = path4url(self.account)
         r = self.head(path, success=(204, 401))
         if r.status_code == 401:
+            r.release()
             raise ClientError("No authorization")
-        return r.headers
+        reply = r.headers
+        r.release()
+        return reply
 
     def replace_account_meta(self, metapairs):
         self.assert_account()
         path = path4url(self.account)
         for key, val in  metapairs:
             self.set_header('X-Account-Meta-'+key, val)
-        self.post(path, success=202)
+        r = self.post(path, success=202)
+        r.release()
 
     def del_account_meta(self, metakey):
         headers = self.get_account_info()
@@ -74,38 +78,49 @@ class StorageClient(Client):
         if len(self.headers) == len(headers):
             raise ClientError('X-Account-Meta-%s not found' % metakey, 404)
         path = path4url(self.account)
-        self.post(path, success = 202)
+        r = self.post(path, success = 202)
+        r.release()
 
     def create_container(self, container):
         self.assert_account()
         path = path4url(self.account, container)
         r = self.put(path, success=(201, 202))
         if r.status_code == 202:
+            r.release()
             raise ClientError("Container already exists", r.status_code)
+        r.release()
 
     def get_container_info(self, container):
         self.assert_account()
         path = path4url(self.account, container)
         r = self.head(path, success=(204, 404))
         if r.status_code == 404:
+            r.release()
             raise ClientError("Container does not exist", r.status_code)
-        return r.headers
+        reply = r.headers
+        r.release()
+        return reply
 
     def delete_container(self, container):
         self.assert_account()
         path = path4url(self.account, container)
         r = self.delete(path, success=(204, 404, 409))
         if r.status_code == 404:
+            r.release()
             raise ClientError("Container does not exist", r.status_code)
         elif r.status_code == 409:
+            r.release()
             raise ClientError("Container is not empty", r.status_code)
+        r.release()
 
     def list_containers(self):
         self.assert_account()
         self.set_param('format', 'json')
         path = path4url(self.account)
         r = self.get(path, success = (200, 204))
-        return r.json
+        reply = r.json
+        r.release()
+        return reply
 
     def upload_object(self, object, f, size=None):
         # This is a naive implementation, it loads the whole file in memory
@@ -113,20 +128,24 @@ class StorageClient(Client):
         self.assert_container()
         path = path4url(self.account, self.container, object)
         data = f.read(size) if size is not None else f.read()
-        self.put(path, data=data, success=201)
+        r = self.put(path, data=data, success=201)
+        r.release()
 
     def create_directory(self, object):
         self.assert_container()
         path = path4url(self.account, self.container, object)
         self.set_header('Content-Type', 'application/directory')
         self.set_header('Content-length', '0')
-        self.put(path, success=201)
+        r = self.put(path, success=201)
+        r.release()
 
     def get_object_info(self, object):
         self.assert_container()
         path = path4url(self.account, self.container, object)
         r = self.head(path, success=200)
-        return r.headers
+        reply = r.headers
+        r.release()
+        return reply
 
     def get_object_meta(self, object):
         r = filter_in(self.get_object_info(object), 'X-Object-Meta-')
@@ -140,21 +159,25 @@ class StorageClient(Client):
         self.assert_container()
         self.set_header('X-Object-Meta-'+metakey, '')
         path = path4url(self.account, self.container, object)
-        self.post(path, success = 202)
+        r = self.post(path, success = 202)
+        r.release()
 
     def replace_object_meta(self, metapairs):
         self.assert_container()
         path=path4url(self.account, self.container)
         for key, val in metapairs:
             self.set_header('X-Object-Meta-'+key, val)
-        self.post(path, success=202)
+        r = self.post(path, success=202)
+        r.release()
 
     def get_object(self, object):
         self.assert_container()
         path = path4url(self.account, self.container, object)
         r = self.get(path, success=200)
         size = int(r.headers['content-length'])
-        return r.content, size
+        cnt = r.content
+        r.release()
+        return cnt, size
 
     def copy_object(self, src_container, src_object, dst_container, dst_object=False):
         self.assert_account()
@@ -162,7 +185,8 @@ class StorageClient(Client):
         dst_path = path4url(self.account, dst_container, dst_object)
         self.set_header('X-Copy-From', path4url(src_container, src_object))
         self.set_header('Content-Length', 0)
-        self.put(dst_path, success=201)
+        r = self.put(dst_path, success=201)
+        r.release()
 
     def move_object(self, src_container, src_object, dst_container, dst_object=False):
         self.assert_account()
@@ -170,14 +194,17 @@ class StorageClient(Client):
         dst_path = path4url(self.account, dst_container, dst_object)
         self.set_header('X-Move-From', path4url(src_container, src_object))
         self.set_header('Content-Length', 0)
-        self.put(dst_path, success=201)
+        r = self.put(dst_path, success=201)
+        r.release()
 
     def delete_object(self, object):
         self.assert_container()
         path = path4url(self.account, self.container, object)
         r = self.delete(path, success=(204, 404))
         if r.status_code == 404:
+            r.release()
             raise ClientError("Object %s not found" %object, r.status_code)
+        r.release()
        
     def list_objects(self):
         self.assert_container()
@@ -185,10 +212,14 @@ class StorageClient(Client):
         self.set_param('format', 'json')
         r = self.get(path, success=(200, 204, 304, 404), )
         if r.status_code == 404:
+            r.release()
             raise ClientError("Incorrect account (%s) for that container"%self.account, r.status_code)
         elif r.status_code == 304:
+            r.release()
             return []
-        return r.json
+        reply = r.json
+        r.release()
+        return reply
 
     def list_objects_in_path(self, path_prefix):
         self.assert_container()
@@ -197,6 +228,9 @@ class StorageClient(Client):
         self.set_param('path', 'path_prefix')
         r = self.get(path, success=(200, 204, 404))
         if r.status_code == 404:
+            r.release()
             raise ClientError("Incorrect account (%s) for that container"%self.account, r.status_code)
-        return r.json
+        reply = r.json
+        r.release()
+        return reply
 
index 6adad12..1373626 100644 (file)
@@ -263,10 +263,15 @@ class testPithos(unittest.TestCase):
         token = 'C/yBXmz3XjTFBnujc2biAg=='
         token = 'ac0yH8cQMEZu3M3Mp1MWGA=='
         account = 'admin@adminland.com'
-        """
+
         url='https://pithos.okeanos.grnet.gr/v1'
         token='MI6PT0yrXJ9Ji/x8l9Wmig=='
         account='saxtouri@gmail.com'
+        """
+
+        url='https://pithos.okeanos.io/v1'
+        token='0TpoyAXqJSPxLdDuZHiLOA=='
+        account='saxtouri@admin.grnet.gr'
         
 
         self.fname = None
@@ -286,23 +291,26 @@ class testPithos(unittest.TestCase):
         self.makeNewObject(self.c2, 'test1')
         """Prepare an object to be shared - also its container"""
         self.client.container = self.c1
-        self.client.object_post('test', update=True, permitions={'read':'someUser'})
+        r = self.client.object_post('test', update=True, permitions={'read':'someUser'})
+        r.release()
         self.makeNewObject(self.c1, 'another.test')
 
     def makeNewObject(self, container, obj):
         self.client.container = container
-        self.client.object_put(obj, content_type='application/octet-stream',
+        r = self.client.object_put(obj, content_type='application/octet-stream',
             data= 'file '+obj+' that lives in '+container,
             metadata={'incontainer':container})
+        r.release()
 
     def forceDeleteContainer(self, container):
         self.client.container = container
         r = self.client.list_objects()
         for obj in r:
             name = obj['name']
-            self.client.object_delete(name)
-        self.client.container_delete()
+            self.client.del_object(name)
+        r = self.client.container_delete()
         self.container = ''
+        r.release()
 
     def tearDown(self):
         """Destroy test cases"""
@@ -327,8 +335,10 @@ class testPithos(unittest.TestCase):
         """Test account_HEAD"""
         r = self.client.account_head()
         self.assertEqual(r.status_code, 204)
+        r.release()
         r = self.client.account_head(until='1000000000')
         self.assertEqual(r.status_code, 204)
+        r.release()
         datestring = unicode(r.headers['x-account-until-timestamp'])
         self.assertEqual(u'Sun, 09 Sep 2001 01:46:40 GMT', datestring)
 
@@ -336,8 +346,12 @@ class testPithos(unittest.TestCase):
         for format in self.client.DATE_FORMATS:
             now_formated = self.now_unformated.strftime(format)
             r1 = self.client.account_head(if_modified_since=now_formated, success=(204, 304, 412))
+            sc1 = r1.status_code
+            r1.release()
             r2 = self.client.account_head(if_unmodified_since=now_formated, success=(204, 304, 412))
-            self.assertNotEqual(r1.status_code, r2.status_code)
+            sc2 = r2.status_code
+            r2.release()
+            self.assertNotEqual(sc1, sc2)
 
     def test_account_get(self):
         """Test account_GET"""
@@ -345,37 +359,48 @@ class testPithos(unittest.TestCase):
         self.assertEqual(r.status_code, 200)
         fullLen = len(r.json)
         self.assertTrue(fullLen > 2)
+        r.release()
 
         r = self.client.account_get(limit=1)
         self.assertEqual(len(r.json), 1)
+        r.release()
 
         r = self.client.account_get(marker='c2_')
         temp_c0 = r.json[0]['name']
         temp_c2 = r.json[2]['name']
+        r.release()
         r = self.client.account_get(limit=2, marker='c2_')
         conames = [container['name'] for container in r.json \
             if container['name'].lower().startswith('c2_')]
         self.assertTrue(temp_c0 in conames)
         self.assertFalse(temp_c2 in conames)
+        r.release()
 
         r = self.client.account_get(show_only_shared=True)
         self.assertTrue(self.c1 in [c['name'] for c in r.json])
+        r.release()
 
         r = self.client.account_get(until=1342609206)
         self.assertTrue(len(r.json) <= fullLen)
+        r.release()
 
         """Check if(un)modified_since"""
         for format in self.client.DATE_FORMATS:
             now_formated = self.now_unformated.strftime(format)
             r1 = self.client.account_get(if_modified_since=now_formated, success=(200, 304, 412))
+            sc1 = r1.status_code
+            r1.release()
             r2 = self.client.account_get(if_unmodified_since=now_formated, success=(200, 304, 412))
-            self.assertNotEqual(r1.status_code, r2.status_code)
+            sc2 = r2.status_code
+            r2.release()
+            self.assertNotEqual(sc1, sc2)
 
     def test_account_post(self):
         """Test account_POST"""
         r = self.client.account_post()
         self.assertEqual(r.status_code, 202)
         grpName = 'grp'+unicode(self.now)
+        r.release()
 
         """Method set/del_account_meta and set_account_groupcall use account_post internally
         """
@@ -410,17 +435,23 @@ class testPithos(unittest.TestCase):
 
         r = self.client.container_head()
         self.assertEqual(r.status_code, 204)
+        r.release()
 
         """Check until"""
         r = self.client.container_head(until=1000000, success=(204, 404))
         self.assertEqual(r.status_code, 404)
+        r.release()
 
         """Check and if(un)modified_since"""
         for format in self.client.DATE_FORMATS:
             now_formated = self.now_unformated.strftime(format)
             r1 = self.client.container_head(if_modified_since=now_formated, success=(204, 304, 412))
+            sc1=r1.status_code
+            r1.release()
             r2 = self.client.container_head(if_unmodified_since=now_formated, success=(204, 304, 412))
-            self.assertNotEqual(r1.status_code, r2.status_code)
+            sc2=r2.status_code
+            r2.release()
+            self.assertNotEqual(sc1, sc2)
 
     def test_container_get(self):
         """Test container_GET"""
@@ -429,48 +460,63 @@ class testPithos(unittest.TestCase):
         r = self.client.container_get()
         self.assertEqual(r.status_code, 200)
         fullLen = len(r.json)
+        r.release()
 
         r = self.client.container_get(prefix='test')
         lalobjects = [obj for obj in r.json if obj['name'].startswith('test')]
         self.assertTrue(len(r.json) > 1)
         self.assertEqual(len(r.json), len(lalobjects))
+        r.release()
 
         r = self.client.container_get(limit=1)
         self.assertEqual(len(r.json), 1)
+        r.release()
 
         r = self.client.container_get(marker='another')
         self.assertTrue(len(r.json) > 1)
         neobjects = [obj for obj in r.json if obj['name'] > 'another']
         self.assertEqual(len(r.json), len(neobjects))
+        r.release()
 
         r = self.client.container_get(prefix='another.test', delimiter='.')
         self.assertTrue(fullLen > len(r.json))
+        r.release()
 
         r = self.client.container_get(path='/')
         self.assertEqual(fullLen, len(r.json))
+        r.release()
 
         r = self.client.container_get(format='xml')
         self.assertEqual(r.text.split()[4], 'name="'+self.c1+'">')
+        r.release()
 
         r = self.client.container_get(meta=['incontainer'])
         self.assertTrue(len(r.json) > 0)
+        r.release()
 
         r = self.client.container_get(show_only_shared=True)
         self.assertTrue(len(r.json) < fullLen)
+        r.release()
 
         try:
             r = self.client.container_get(until=1000000000)
             datestring = unicode(r.headers['x-account-until-timestamp'])
             self.assertEqual(u'Sun, 09 Sep 2001 01:46:40 GMT', datestring)
-        except:#Normally, container wasn't created in that date...
+            r.release()
+        except ClientError:
+            r.release()
             pass
 
-        """Check and if(un)modified_since"""
+        """Check and if un/modified_since"""
         for format in self.client.DATE_FORMATS:
             now_formated = self.now_unformated.strftime(format)
             r1 = self.client.container_get(if_modified_since=now_formated, success=(200, 304, 412))
+            sc1 = r1.status_code
+            r1.release()
             r2 = self.client.container_get(if_unmodified_since=now_formated, success=(200, 304, 412))
-            self.assertNotEqual(r1.status_code, r2.status_code)
+            sc2 = r2.status_code
+            r2.release()
+            self.assertNotEqual(sc1, sc2)
        
     def test_container_put(self):
         """Test container_PUT"""
@@ -478,6 +524,7 @@ class testPithos(unittest.TestCase):
 
         r = self.client.container_put()
         self.assertEqual(r.status_code, 202)
+        r.release()
 
         r = self.client.get_container_quota(self.client.container)
         cquota = r.values()[0]
@@ -485,24 +532,28 @@ class testPithos(unittest.TestCase):
 
         r = self.client.container_put(quota=newquota)
         self.assertEqual(r.status_code, 202)
+        r.release()
         r = self.client.get_container_quota(self.client.container)
         xquota = int(r.values()[0])
         self.assertEqual(newquota, xquota)
 
         r = self.client.container_put(versioning='auto')
         self.assertEqual(r.status_code, 202)
+        r.release()
         r = self.client.get_container_versioning(self.client.container)
         nvers = r.values()[0]
         self.assertEqual('auto', nvers)
 
         r = self.client.container_put(versioning='none')
         self.assertEqual(r.status_code, 202)
+        r.release()
         r = self.client.get_container_versioning(self.client.container)
         nvers = r.values()[0]
         self.assertEqual('none', nvers)
 
         r = self.client.container_put(metadata={'m1':'v1', 'm2':'v2'})
         self.assertEqual(r.status_code, 202)
+        r.release()
         r = self.client.get_container_meta(self.client.container)
         self.assertTrue(r.has_key('x-container-meta-m1'))
         self.assertEqual(r['x-container-meta-m1'], 'v1')
@@ -511,6 +562,7 @@ class testPithos(unittest.TestCase):
 
         r = self.client.container_put(metadata={'m1':'', 'm2':'v2a'})
         self.assertEqual(r.status_code, 202)
+        r.release()
         r = self.client.get_container_meta(self.client.container)
         self.assertTrue(not r.has_key('x-container-meta-m1'))
         self.assertTrue(r.has_key('x-container-meta-m2'))
@@ -525,6 +577,7 @@ class testPithos(unittest.TestCase):
         """Simple post"""
         r = self.client.container_post()
         self.assertEqual(r.status_code, 202)
+        r.release()
 
         """post meta"""
         self.client.set_container_meta({'m1':'v1', 'm2':'v2'})
@@ -583,6 +636,7 @@ class testPithos(unittest.TestCase):
 
         """Check update=False"""
         r = self.client.object_post('test', update=False, metadata={'newmeta':'newval'})
+        r.release()
         r = self.client.get_object_info('test')
         self.assertTrue(r.has_key('x-object-meta-newmeta'))
         self.assertFalse(r.has_key('x-object-meta-incontainer'))
@@ -596,15 +650,18 @@ class testPithos(unittest.TestCase):
         self.client.container = self.c2
         r = self.client.container_delete(success=409)
         self.assertEqual(r.status_code, 409)
+        r.release()
 
         """Fail to delete c3 (empty) container"""
         self.client.container = self.c3
         r = self.client.container_delete(until='1000000000')
         self.assertEqual(r.status_code, 204)
+        r.release()
 
         """Delete c3 (empty) container"""
         r = self.client.container_delete()
         self.assertEqual(r.status_code, 204)
+        r.release()
 
     def test_object_head(self):
         """Test object_HEAD"""
@@ -614,24 +671,35 @@ class testPithos(unittest.TestCase):
         r = self.client.object_head(obj)
         self.assertEqual(r.status_code, 200)
         etag = r.headers['etag']
+        r.release()
 
         r = self.client.object_head(obj, version=40)
         self.assertEqual(r.headers['x-object-version'], '40')
+        r.release()
 
         r = self.client.object_head(obj, if_etag_match=etag)
         self.assertEqual(r.status_code, 200)
+        r.release()
         r = self.client.object_head(obj, if_etag_not_match=etag, success=(200, 412, 304))
         self.assertNotEqual(r.status_code, 200)
+        r.release()
 
         r = self.client.object_head(obj, version=40, if_etag_match=etag, success=412)
         self.assertEqual(r.status_code, 412)
+        r.release()
 
         """Check and if(un)modified_since"""
         for format in self.client.DATE_FORMATS:
             now_formated = self.now_unformated.strftime(format)
-            r1 = self.client.object_head(obj, if_modified_since=now_formated, success=(200, 304, 412))
-            r2 = self.client.object_head(obj, if_unmodified_since=now_formated, success=(200, 304, 412))
-            self.assertNotEqual(r1.status_code, r2.status_code)
+            r1 = self.client.object_head(obj, if_modified_since=now_formated,
+                success=(200, 304, 412))
+            sc1 = r1.status_code
+            r1.release()
+            r2 = self.client.object_head(obj, if_unmodified_since=now_formated,
+                success=(200, 304, 412))
+            sc2 = r2.status_code
+            r2.release()
+            self.assertNotEqual(sc1, sc2)
 
     def test_object_get(self):
         """Test object_GET"""
@@ -643,38 +711,50 @@ class testPithos(unittest.TestCase):
 
         osize = int(r.headers['content-length'])
         etag = r.headers['etag']
+        r.release()
 
         r = self.client.object_get(obj, hashmap=True)
         self.assertTrue(r.json.has_key('hashes') \
             and r.json.has_key('block_hash') \
             and r.json.has_key('block_size') \
             and r.json.has_key('bytes'))
+        r.release()
 
         r = self.client.object_get(obj, format='xml', hashmap=True)
         self.assertEqual(len(r.text.split('hash>')), 3)
+        r.release()
 
         rangestr = 'bytes=%s-%s'%(osize/3, osize/2)
         r = self.client.object_get(obj, data_range=rangestr, success=(200, 206))
         partsize = int(r.headers['content-length'])
         self.assertTrue(0 < partsize and partsize <= 1+osize/3)
+        r.release()
 
         rangestr = 'bytes=%s-%s'%(osize/3, osize/2)
         r = self.client.object_get(obj, data_range=rangestr, if_range=True, success=(200, 206))
         partsize = int(r.headers['content-length'])
         self.assertTrue(0 < partsize and partsize <= 1+osize/3)
+        r.release()
 
         r = self.client.object_get(obj, if_etag_match=etag)
         self.assertEqual(r.status_code, 200)
+        r.release()
 
         r = self.client.object_get(obj, if_etag_not_match=etag+'LALALA')
         self.assertEqual(r.status_code, 200)
+        r.release()
 
         """Check and if(un)modified_since"""
         for format in self.client.DATE_FORMATS:
             now_formated = self.now_unformated.strftime(format)
-            r1 = self.client.object_get(obj, if_modified_since=now_formated, success=(200, 304, 412))
+            r1 = self.client.object_get(obj, if_modified_since=now_formated,
+                success=(200, 304, 412))
+            sc1 = r1.status_code
+            r1.release()
             r2 = self.client.object_get(obj, if_unmodified_since=now_formated, success=(200, 304, 412))
-            self.assertNotEqual(r1.status_code, r2.status_code)
+            sc2 = r2.status_code
+            r2.release()
+            self.assertNotEqual(sc1, sc2)
 
     def test_object_put(self):
         """Test object_PUT"""
@@ -689,6 +769,7 @@ class testPithos(unittest.TestCase):
             content_disposition='attachment; filename="fname.ext"')
         self.assertEqual(r.status_code, 201)
         etag = r.headers['etag']
+        r.release()
 
         """Check content-disposition"""
         r = self.client.get_object_info(obj)
@@ -709,21 +790,25 @@ class testPithos(unittest.TestCase):
         """Check public and if_etag_match"""
         r = self.client.object_put(obj, if_etag_match=etag, data='b',
             content_type='application/octet-stream', public=True)
+        r.release()
         r = self.client.object_get(obj)
         self.assertTrue(r.headers.has_key('x-object-public'))
         vers2 = int(r.headers['x-object-version'])
         etag = r.headers['etag']
         self.assertEqual(r.text, 'b')
+        r.release()
 
         """Check if_etag_not_match"""
         r = self.client.object_put(obj, if_etag_not_match=etag, data='c',
             content_type='application/octet-stream', success=(201, 412))
         self.assertEqual(r.status_code, 412)
+        r.release()
 
         """Check content_type and content_length"""
         tmpdir = 'dir'+unicode(self.now)
         r = self.client.object_put(tmpdir, content_type='application/directory',
             content_length=0)
+        r.release()
         r = self.client.get_object_info(tmpdir)
         self.assertEqual(r['content-type'], 'application/directory')
 
@@ -734,6 +819,7 @@ class testPithos(unittest.TestCase):
             source_account=self.client.account,
             content_length=0, success=201)
         self.assertEqual(r.status_code, 201)
+        r.release()
 
         """Check cross-container copy_from, content_encoding"""
         self.client.container = self.c1
@@ -742,6 +828,7 @@ class testPithos(unittest.TestCase):
             content_encoding='application/octet-stream', 
             source_account=self.client.account,
             content_length=0, success=201)
+        r.release()
         self.assertEqual(r.status_code, 201)
         r = self.client.get_object_info(obj)
         self.assertEqual(r['etag'], etag)
@@ -754,6 +841,7 @@ class testPithos(unittest.TestCase):
             source_account='nonExistendAddress@NeverLand.com', 
             content_length=0, success=(201, 403))
         self.assertEqual(r.status_code, 403)
+        r.release()
 
         """Check cross-container move_from"""
         r = self.client.object_put(obj+'v0', format=None, 
@@ -761,6 +849,7 @@ class testPithos(unittest.TestCase):
             content_encoding='application/octet-stream', 
             content_length=0, success=201)
         self.assertEqual(r.status_code, 201)
+        r.release()
         r = self.client.get_object_info(obj+'v0')
         self.assertEqual(r['etag'], etag)
 
@@ -770,6 +859,7 @@ class testPithos(unittest.TestCase):
             source_version = vers2,
             content_encoding='application/octet-stream',
             content_length=0, success=201)
+        r.release()
 
         """Check manifest"""
         mobj = 'manifest.test'
@@ -777,12 +867,14 @@ class testPithos(unittest.TestCase):
         for i in range(10):
             txt += '%s'%i
             r = self.client.object_put('%s/%s'%(mobj, i), data='%s'%i,
-                content_encoding='application/octet-stream',
-                content_length=1, success=201)
-        self.client.object_put(mobj, content_length=0,
+                content_encoding='application/octet-stream', content_length=1, success=201)
+            r.release()
+        r = self.client.object_put(mobj, content_length=0, content_type='application/octet-stream',
             manifest='%s/%s'%(self.client.container, mobj))
+        r.release()
         r = self.client.object_get(mobj)
         self.assertEqual(r.text, txt)
+        r.release()
 
         """Some problems with transfer-encoding?"""
 
@@ -798,12 +890,14 @@ class testPithos(unittest.TestCase):
                 'read':['accX:groupA', 'u1', 'u2'],
                 'write':['u2', 'u3']},
             content_disposition='attachment; filename="fname.ext"')
+        r.release()
         r = self.client.object_copy(obj+'orig',
             destination = '/'+self.client.container+'/'+obj,
             ignore_content_type=False, content_type='application/json', 
             metadata={'mkey2':'mval2a', 'mkey3':'mval3'},
             permitions={'write':['u5', 'accX:groupB']})
         self.assertEqual(r.status_code, 201)
+        r.release()
 
         """Check content-disposition"""
         r = self.client.get_object_info(obj)
@@ -825,6 +919,7 @@ class testPithos(unittest.TestCase):
             content_type='application/json', destination_account='nonExistendAddress@NeverLand.com',
             success=(201, 403))
         self.assertEqual(r.status_code, 403)
+        r.release()
 
         """Check destination being another container
         and also content_type and content encoding"""
@@ -832,31 +927,37 @@ class testPithos(unittest.TestCase):
             content_encoding='utf8', content_type='application/json')
         self.assertEqual(r.status_code, 201)
         self.assertEqual(r.headers['content-type'], 'application/json; charset=UTF-8')
+        r.release()
 
         """Check ignore_content_type and content_type"""
         r = self.client.object_get(obj)
         etag = r.headers['etag']
         ctype = r.headers['content-type']
         self.assertEqual(ctype, 'application/json')
+        r.release()
         r = self.client.object_copy(obj+'orig',
             destination = '/'+self.client.container+'/'+obj+'0',
             ignore_content_type=True, content_type='application/json')
         self.assertEqual(r.status_code, 201)
         self.assertNotEqual(r.headers['content-type'], 'application/json')
+        r.release()
 
         """Check if_etag_(not_)match"""
         r = self.client.object_copy(obj,
             destination='/'+self.client.container+'/'+obj+'1', if_etag_match=etag)
         self.assertEqual(r.status_code, 201)
+        r.release()
         r = self.client.object_copy(obj,
             destination='/'+self.client.container+'/'+obj+'2', if_etag_not_match='lalala')
         self.assertEqual(r.status_code, 201)
         vers2 = r.headers['x-object-version']
+        r.release()
 
         """Check source_version, public and format """
         r = self.client.object_copy(obj+'2', destination='/'+self.client.container+'/'+obj+'3', source_version=vers2, format='xml', public=True)
         self.assertEqual(r.status_code, 201)
         self.assertTrue(r.headers['content-type'].index('xml') > 0)
+        r.release()
         r = self.client.get_object_info(obj+'3')
         self.assertTrue(r.has_key('x-object-public'))
 
@@ -869,11 +970,13 @@ class testPithos(unittest.TestCase):
         r = self.client.object_put(obj+'orig', content_type='application/octet-stream',
             data= data, metadata={'mkey1':'mval1', 'mkey2':'mval2'},
             permitions={'read':['accX:groupA', 'u1', 'u2'], 'write':['u2', 'u3']})
+        r.release()
         r = self.client.object_move(obj+'orig', destination = '/'+self.client.container+'/'+obj,
             ignore_content_type=False, content_type='application/json', 
             metadata={'mkey2':'mval2a', 'mkey3':'mval3'},
             permitions={'write':['u5', 'accX:groupB']})
         self.assertEqual(r.status_code, 201)
+        r.release()
 
         """Check Metadata"""
         r = self.client.get_object_meta(obj)
@@ -892,6 +995,7 @@ class testPithos(unittest.TestCase):
             content_type='application/json', destination_account='nonExistendAddress@NeverLand.com',
             success=(201, 403))
         self.assertEqual(r.status_code, 403)
+        r.release()
 
         """Check destination being another container and also
         content_type, content_disposition and content encoding"""
@@ -900,7 +1004,7 @@ class testPithos(unittest.TestCase):
             content_disposition='attachment; filename="fname.ext"')
         self.assertEqual(r.status_code, 201)
         self.assertEqual(r.headers['content-type'], 'application/json; charset=UTF-8')
-        r = self.client.container=self.c1
+        self.client.container=self.c1
         r = self.client.get_object_info(obj)
         self.assertTrue(r.has_key('content-disposition') and 'fname.ext' in r['content-disposition'])
         etag = r['etag']
@@ -912,21 +1016,25 @@ class testPithos(unittest.TestCase):
             ignore_content_type=True, content_type='application/json')
         self.assertEqual(r.status_code, 201)
         self.assertNotEqual(r.headers['content-type'], 'application/json')
+        r.release()
 
         """Check if_etag_(not_)match"""
-        r = self.client.container=self.c2
+        self.client.container=self.c2
         r = self.client.object_move(obj, destination='/'+self.client.container+'/'+obj+'0',
             if_etag_match=etag)
         self.assertEqual(r.status_code, 201)
+        r.release()
         r = self.client.object_move(obj+'0', destination='/'+self.client.container+'/'+obj+'1',
             if_etag_not_match='lalala')
         self.assertEqual(r.status_code, 201)
+        r.release()
 
         """Check public and format """
         r = self.client.object_move(obj+'1', destination='/'+self.client.container+'/'+obj+'2',
             format='xml', public=True)
         self.assertEqual(r.status_code, 201)
         self.assertTrue(r.headers['content-type'].index('xml') > 0)
+        r.release()
         r = self.client.get_object_info(obj+'2')
         self.assertTrue(r.has_key('x-object-public'))
 
@@ -943,12 +1051,14 @@ class testPithos(unittest.TestCase):
         r = self.client.object_put(obj, content_type='application/octet-stream',
             data= 'H', metadata={'mkey1':'mval1', 'mkey2':'mval2'},
             permitions={'read':['accX:groupA', 'u1', 'u2'], 'write':['u2', 'u3']})
+        r.release()
 
         """Append tests update, content_range, content_type, content_length"""
         newf = open(obj, 'r')
         self.client.append_object(obj, newf)
         r = self.client.object_get(obj)
         self.assertTrue(r.text.startswith('Hello!'))
+        r.release()
 
         """Overwrite tests update, content_type, content_length, content_range"""
         newf.seek(0)
@@ -956,12 +1066,14 @@ class testPithos(unittest.TestCase):
         r = self.client.object_get(obj)
         self.assertTrue(r.text.startswith('ello!'))
         newf.close()
+        r.release()
         
         """Truncate tests update, content_range, content_type,
         object_bytes and source_object"""
         r = self.client.truncate_object(obj, 5)
         r = self.client.object_get(obj)
         self.assertEqual(r.text, 'ello!')
+        r.release()
 
         """Check metadata"""
         self.client.set_object_meta(obj, {'mkey2':'mval2a', 'mkey3':'mval3'})
@@ -998,8 +1110,10 @@ class testPithos(unittest.TestCase):
         r = self.client.object_post(obj, update=True, public=True,
             if_etag_not_match=etag, success=(412,202,204))
         self.assertEqual(r.status_code, 412)
-        self.client.object_post(obj, update=True, public=True,
+        r.release()
+        r = self.client.object_post(obj, update=True, public=True,
             if_etag_match=etag, content_encoding='application/json')
+        r.release()
         r = self.client.get_object_info(obj)
         helloVersion = r['x-object-version']
         self.assertTrue(r.has_key('x-object-public'))
@@ -1011,14 +1125,17 @@ class testPithos(unittest.TestCase):
             source_account='thisAccountWillNeverExist@adminland.com',
             source_version=helloVersion, data='12345', success=(403, 202, 204))
         self.assertEqual(r.status_code, 403)
+        r.release()
         r = self.client.object_post(obj, update=True, content_type='application/octet-srteam',
             content_length=5, content_range='bytes 1-5/*', source_object='/%s/%s'%(self.c2,obj),
             source_account=self.client.account, source_version=helloVersion, data='12345',
             content_disposition='attachment; filename="fname.ext"')
+        r.release()
         r = self.client.object_get(obj)
         self.assertEqual(r.text, 'eello!')
         self.assertTrue(r.headers.has_key('content-disposition')
             and 'fname.ext' in r.headers['content-disposition'])
+        r.release()
 
         """Check manifest"""
         mobj = 'manifest.test'
@@ -1028,10 +1145,14 @@ class testPithos(unittest.TestCase):
             r = self.client.object_put('%s/%s'%(mobj, i), data='%s'%i,
                 content_encoding='application/octet-stream',
                 content_length=1, success=201)
-        self.client.object_put(mobj, content_length=0)
+            r.release()
+        r = self.client.object_put(mobj, content_length=0, content_type='application/octet-stream')
+        r.release()
         r = self.client.object_post(mobj, manifest='%s/%s'%(self.client.container, mobj))
+        r.release()
         r = self.client.object_get(mobj)
         self.assertEqual(r.text, txt)
+        r.release()
 
         """We need to check transfer_encoding """
 
@@ -1043,17 +1164,22 @@ class testPithos(unittest.TestCase):
         r = self.client.object_put(obj, content_type='application/octet-stream',
             data= 'H', metadata={'mkey1':'mval1', 'mkey2':'mval2'},
             permitions={'read':['accX:groupA', 'u1', 'u2'], 'write':['u2', 'u3']})
+        r.release()
 
         """Check with false until"""
         r = self.client.object_delete(obj, until=1000000)
+        r.release()
         r = self.client.object_get(obj, success=(200, 404))
         self.assertEqual(r.status_code, 200)
+        r.release()
 
         """Check normal case"""
         r = self.client.object_delete(obj)
         self.assertEqual(r.status_code, 204)
+        r.release()
         r = self.client.object_get(obj, success=(200, 404))
         self.assertEqual(r.status_code, 404)
+        r.release()
 
     def create_large_file(self, size, name):
         """Create a large file at fs"""