Merged httplib2 change from head into cmislib_refactor branch.

git-svn-id: https://svn.apache.org/repos/asf/chemistry/cmislib/branches/binding_refactor@1450920 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/cmislib/atompub_binding.py b/src/cmislib/atompub_binding.py
index 886d0ea..0f579d1 100644
--- a/src/cmislib/atompub_binding.py
+++ b/src/cmislib/atompub_binding.py
@@ -114,16 +114,16 @@
         if (len(self.extArgs) > 0):
             kwargs.update(self.extArgs)
 
-        result = Rest().get(url,
+        resp, content = Rest().get(url,
                             username=username,
                             password=password,
                             **kwargs)
-        if type(result) == HTTPError:
-            self._processCommonErrors(result)
-            return result
+        if resp['status'] != '200':
+            self._processCommonErrors(resp, url)
+            return content
         else:
             try:
-                return minidom.parse(result)
+                return minidom.parseString(content)
             except ExpatError:
                 raise CmisException('Could not parse server response', url)
 
@@ -141,13 +141,13 @@
         if (len(self.extArgs) > 0):
             kwargs.update(self.extArgs)
 
-        result = Rest().delete(url,
+        resp, content = Rest().delete(url,
                                username=username,
                                password=password,
                                **kwargs)
-        if type(result) == HTTPError:
-            self._processCommonErrors(result)
-            return result
+        if resp['status'] != '200':
+            self._processCommonErrors(resp, url)
+            return content
         else:
             pass
 
@@ -166,25 +166,25 @@
         if (len(self.extArgs) > 0):
             kwargs.update(self.extArgs)
 
-        result = Rest().post(url,
+        resp, content = Rest().post(url,
                              payload,
                              contentType,
                              username=username,
                              password=password,
                              **kwargs)
-        if type(result) != HTTPError:
+        if resp['status'] == '200':
             try:
-                return minidom.parse(result)
+                return minidom.parseString(content)
             except ExpatError:
                 raise CmisException('Could not parse server response', url)
-        elif result.code == 201:
+        elif resp['status'] == '201':
             try:
-                return minidom.parse(result)
+                return minidom.parseString(content)
             except ExpatError:
                 raise CmisException('Could not parse server response', url)
         else:
-            self._processCommonErrors(result)
-            return result
+            self._processCommonErrors(resp, url)
+            return resp
 
     def put(self, url, username, password, payload, contentType, **kwargs):
 
@@ -201,19 +201,19 @@
         if (len(self.extArgs) > 0):
             kwargs.update(self.extArgs)
 
-        result = Rest().put(url,
+        resp, content = Rest().put(url,
                             payload,
                             contentType,
                             username=username,
                             password=password,
                             **kwargs)
-        if type(result) == HTTPError:
-            self._processCommonErrors(result)
-            return result
+        if resp['status'] != '200' and resp['status'] != '201':
+            self._processCommonErrors(resp, url)
+            return content
         else:
             #if result.headers['content-length'] != '0':
             try:
-                return minidom.parse(result)
+                return minidom.parseString(content)
             except ExpatError:
                 # This may happen and is normal
                 return None
@@ -425,9 +425,6 @@
                                               self._cmisClient.password,
                                               **kwargs)
 
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
-
         # return the result set
         return AtomPubResultSet(self._cmisClient, self._repository, result)
 
@@ -638,8 +635,6 @@
                                                self.xmlDoc.toxml(encoding='utf-8'),
                                                ATOM_XML_ENTRY_TYPE,
                                                **args)
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
 
     def delete(self, **kwargs):
 
@@ -661,9 +656,6 @@
                                          self._cmisClient.password,
                                          **kwargs)
 
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
-
     def applyPolicy(self, policyId):
 
         """
@@ -707,9 +699,6 @@
                                                xmlDoc.toxml(encoding='utf-8'),
                                                ATOM_XML_TYPE)
 
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
-
         # instantiate CmisObject objects with the results and return the list
         entryElements = result.getElementsByTagNameNS(ATOM_NS, 'entry')
         assert(len(entryElements) == 1), "Expected entry element in result from relationship URL post"
@@ -746,9 +735,6 @@
                                               self._cmisClient.password,
                                               **kwargs)
 
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
-
         # return the result set
         return AtomPubResultSet(self._cmisClient, self._repository, result)
 
@@ -795,8 +781,6 @@
             result = self._cmisClient.binding.get(aclUrl.encode('utf-8'),
                                               self._cmisClient.username,
                                               self._cmisClient.password)
-            if type(result) == HTTPError:
-                raise CmisException(result.code)
             return AtomPubACL(xmlDoc=result)
         else:
             raise NotSupportedException
@@ -828,8 +812,6 @@
                                           self._cmisClient.password,
                                           acl.getXmlDoc().toxml(encoding='utf-8'),
                                           CMIS_ACL_TYPE)
-            if type(result) == HTTPError:
-                raise CmisException(result.code)
             return AtomPubACL(xmlDoc=result)
         else:
             raise NotSupportedException
@@ -1523,8 +1505,6 @@
                                               self._cmisClient.username,
                                               self._cmisClient.password,
                                               **addOptions)
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
 
         # instantiate CmisObject objects with the results and return the list
         entryElements = result.getElementsByTagNameNS(ATOM_NS, 'entry')
@@ -1590,8 +1570,6 @@
                                                self._cmisClient.password,
                                                xmlDoc.toxml(encoding='utf-8'),
                                                CMIS_QUERY_TYPE)
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
 
         # return the result set
         return AtomPubResultSet(self._cmisClient, self, result)
@@ -1650,8 +1628,6 @@
                                               self._cmisClient.username,
                                               self._cmisClient.password,
                                               **kwargs)
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
 
         # return the result set
         return AtomPubChangeEntryResultSet(self._cmisClient, self, result)
@@ -1757,8 +1733,6 @@
                                                self._cmisClient.password,
                                                xmlDoc.toxml(encoding='utf-8'),
                                                ATOM_XML_ENTRY_TYPE)
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
 
         # what comes back is the XML for the new document,
         # so use it to instantiate a new document
@@ -1903,8 +1877,6 @@
                                               self._cmisClient.username,
                                               self._cmisClient.password,
                                               **kwargs)
-        if (type(result) == HTTPError):
-            raise CmisException(result.code)
 
         # return the result set
         return AtomPubResultSet(self._cmisClient, self, result)
@@ -2020,8 +1992,6 @@
             result = self._cmisClient.binding.get(link.encode('utf-8'),
                                               self._cmisClient.username,
                                               self._cmisClient.password)
-            if (type(result) == HTTPError):
-                raise CmisException(result.code)
 
             # return the result
             self._xmlDoc = result
@@ -2249,9 +2219,6 @@
                                                entryXmlDoc.toxml(encoding='utf-8'),
                                                ATOM_XML_ENTRY_TYPE)
 
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
-
         # now that the doc is checked out, we need to refresh the XML
         # to pick up the prop updates related to a checkout
         self.reload()
@@ -2372,9 +2339,6 @@
                                       ATOM_XML_TYPE,
                                       **kwargs)
 
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
-
         return AtomPubDocument(self._cmisClient, self._repository, xmlDoc=result)
 
     def getLatestVersion(self, **kwargs):
@@ -2443,9 +2407,6 @@
                                               self._cmisClient.password,
                                               **kwargs)
 
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
-
         # return the result set
         return AtomPubResultSet(self._cmisClient, self._repository, result)
 
@@ -2479,13 +2440,13 @@
             srcUrl = contentElements[0].attributes['src'].value
 
             # the cmis client class parses non-error responses
-            result = Rest().get(srcUrl.encode('utf-8'),
+            result, content = Rest().get(srcUrl.encode('utf-8'),
                                 username=self._cmisClient.username,
                                 password=self._cmisClient.password,
                                 **self._cmisClient.extArgs)
-            if result.code != 200:
-                raise CmisException(result.code)
-            return result
+            if result['status'] != '200':
+                raise CmisException(result['status'])
+            return StringIO.StringIO(content)
         else:
             # otherwise, try to return the value of the content element
             if contentElements[0].childNodes:
@@ -2536,9 +2497,6 @@
                                       mimetype,
                                       **args)
 
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
-
         # what comes back is the XML for the updated document,
         # which is not required by the spec to be the same document
         # we just updated, so use it to instantiate a new document
@@ -2576,8 +2534,6 @@
                                          self._cmisClient.username,
                                          self._cmisClient.password,
                                          **args)
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
 
     def getRenditions(self):
 
@@ -2633,9 +2589,6 @@
                                               filter='cmis:path',
                                               includeRelativePathSegment=True)
 
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
-
         paths = []
         rs = AtomPubResultSet(self._cmisClient, self._repository, result)
         for res in rs:
@@ -2702,8 +2655,6 @@
                                                self._cmisClient.password,
                                                entryXml.toxml(encoding='utf-8'),
                                                ATOM_XML_ENTRY_TYPE)
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
 
         # what comes back is the XML for the new folder,
         # so use it to instantiate a new folder then return it
@@ -2809,9 +2760,6 @@
                                               self._cmisClient.password,
                                               **kwargs)
 
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
-
         # return the result set
         return AtomPubResultSet(self._cmisClient, self._repository, result)
 
@@ -2897,9 +2845,6 @@
                                               self._cmisClient.password,
                                               **kwargs)
 
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
-
         # return the result set
         return AtomPubResultSet(self._cmisClient, self._repository, result)
 
@@ -2937,9 +2882,6 @@
                                               self._cmisClient.password,
                                               **kwargs)
 
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
-
         # return the result set
         return AtomPubResultSet(self._cmisClient, self, result)
 
@@ -2957,9 +2899,6 @@
                                               self._cmisClient.username,
                                               self._cmisClient.password)
 
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
-
         # return the result set
         return AtomPubFolder(self._cmisClient, self._repository, xmlDoc=result)
 
@@ -2991,9 +2930,6 @@
                                          self._cmisClient.password,
                                          **kwargs)
 
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
-
     def addObject(self, cmisObject, **kwargs):
 
         """
@@ -3029,8 +2965,6 @@
                                                cmisObject.xmlDoc.toxml(encoding='utf-8'),
                                                ATOM_XML_ENTRY_TYPE,
                                                **kwargs)
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
 
     def removeObject(self, cmisObject):
 
@@ -3053,8 +2987,6 @@
                                                cmisObject.xmlDoc.toxml(encoding='utf-8'),
                                                ATOM_XML_ENTRY_TYPE,
                                                **args)
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
 
     def getPaths(self):
         """
@@ -3321,8 +3253,6 @@
                                               self._cmisClient.username,
                                               self._cmisClient.password,
                                               **kwargs)
-        if type(result) == HTTPError:
-            raise CmisException(result.code)
 
         # instantiate CmisObject objects with the results and return the list
         entryElements = result.getElementsByTagNameNS(ATOM_NS, 'entry')
@@ -3758,8 +3688,6 @@
             result = self._cmisClient.binding.get(aclUrl.encode('utf-8'),
                                               self._cmisClient.username,
                                               self._cmisClient.password)
-            if type(result) == HTTPError:
-                raise CmisException(result.code)
             return AtomPubACL(xmlDoc=result)
 
     def getChangeTime(self):
@@ -3908,8 +3836,6 @@
             result = self._cmisClient.binding.get(aclUrl.encode('utf-8'),
                                               self._cmisClient.username,
                                               self._cmisClient.password)
-            if type(result) == HTTPError:
-                raise CmisException(result.code)
             return AtomPubACL(xmlDoc=result)
 
     def getChangeTime(self):
diff --git a/src/cmislib/cmis_services.py b/src/cmislib/cmis_services.py
index bea5212..a78ad8a 100644
--- a/src/cmislib/cmis_services.py
+++ b/src/cmislib/cmis_services.py
@@ -28,7 +28,7 @@
     def getRepositoryService():
         pass
 
-    def _processCommonErrors(self, error):
+    def _processCommonErrors(self, error, url):
 
         """
         Maps HTTPErrors that are common to all to exceptions. Only errors
@@ -36,20 +36,20 @@
         here. Callers should handle the rest.
         """
 
-        if error.status == 401:
-            raise PermissionDeniedException(error.status, error.url)
-        elif error.status == 400:
-            raise InvalidArgumentException(error.status, error.url)
-        elif error.status == 404:
-            raise ObjectNotFoundException(error.status, error.url)
-        elif error.status == 403:
-            raise PermissionDeniedException(error.status, error.url)
-        elif error.status == 405:
-            raise NotSupportedException(error.status, error.url)
-        elif error.status == 409:
-            raise UpdateConflictException(error.status, error.url)
-        elif error.status == 500:
-            raise RuntimeException(error.status, error.url)
+        if error['status'] == '401':
+            raise PermissionDeniedException(error['status'], url)
+        elif error['status'] == '400':
+            raise InvalidArgumentException(error['status'], url)
+        elif error['status'] == '404':
+            raise ObjectNotFoundException(error['status'], url)
+        elif error['status'] == '403':
+            raise PermissionDeniedException(error['status'], url)
+        elif error['status'] == '405':
+            raise NotSupportedException(error['status'], url)
+        elif error['status'] == '409':
+            raise UpdateConflictException(error['status'], url)
+        elif error['status'] == '500':
+            raise RuntimeException(error['status'], url)
 
 
 class RepositoryServiceIfc(object):
diff --git a/src/cmislib/model.py b/src/cmislib/model.py
index cc86368..cb055cc 100644
--- a/src/cmislib/model.py
+++ b/src/cmislib/model.py
@@ -98,28 +98,6 @@
 
         return self.binding.getRepositoryService().getDefaultRepository(self)
 
-    def _processCommonErrors(self, error):
-
-        """
-        Maps HTTPErrors that are common to all to exceptions. Only errors
-        that are truly global, like 401 not authorized, should be handled
-        here. Callers should handle the rest.
-        """
-
-        if error.status == 401:
-            raise PermissionDeniedException(error.status, error.url)
-        elif error.status == 400:
-            raise InvalidArgumentException(error.status, error.url)
-        elif error.status == 404:
-            raise ObjectNotFoundException(error.status, error.url)
-        elif error.status == 403:
-            raise PermissionDeniedException(error.status, error.url)
-        elif error.status == 405:
-            raise NotSupportedException(error.status, error.url)
-        elif error.status == 409:
-            raise UpdateConflictException(error.status, error.url)
-        elif error.status == 500:
-            raise RuntimeException(error.status, error.url)
 
     defaultRepository = property(getDefaultRepository)
     repositories = property(getRepositories)
diff --git a/src/cmislib/net.py b/src/cmislib/net.py
index 11a757a..ab66717 100644
--- a/src/cmislib/net.py
+++ b/src/cmislib/net.py
@@ -21,77 +21,8 @@
 '''
 
 from urllib import urlencode
-from urllib2 import HTTPBasicAuthHandler, \
-                    HTTPPasswordMgrWithDefaultRealm, \
-                    HTTPRedirectHandler, \
-                    HTTPDefaultErrorHandler, \
-                    HTTPError, \
-                    Request, \
-                    build_opener, \
-                    AbstractBasicAuthHandler
 import logging
-
-
-class SmartRedirectHandler(HTTPRedirectHandler):
-
-    """ Handles 301 and 302 redirects """
-
-    def http_error_301(self, req, fp, code, msg, headers):
-        """ Handle a 301 error """
-        result = HTTPRedirectHandler.http_error_301(
-            self, req, fp, code, msg, headers)
-        result.status = code
-        return result
-
-    def http_error_302(self, req, fp, code, msg, headers):
-        """ Handle a 302 error """
-        result = HTTPRedirectHandler.http_error_302(
-            self, req, fp, code, msg, headers)
-        result.status = code
-        return result
-
-
-class DefaultErrorHandler(HTTPDefaultErrorHandler):
-
-    """ Default error handler """
-
-    def http_error_default(self, req, fp, code, msg, headers):
-        """Provide an implementation for the default handler"""
-        result = HTTPError(
-            req.get_full_url(), code, msg, headers, fp)
-        result.status = code
-        return result
-
-
-class ContextualBasicAuthHandler(HTTPBasicAuthHandler):
-
-    """
-    Handles 401 errors without recursing indefinitely. The recursing
-    behaviour has been introduced in Python 2.6.5 to handle 401 redirects
-    used by some architectures of authentication.
-    """
-
-    def __init__(self, password_mgr):
-        HTTPBasicAuthHandler.__init__(self, password_mgr)
-        self.authContext = set([])
-
-    def http_error_401(self, req, fp, code, msg, headers):
-        """Override the default autoretry behaviour"""
-        url = req.get_full_url()
-        hdrs = req.header_items()
-        hdrs = ', '.join(['%s: %s' % (key, value)
-                          for key, value in sorted(hdrs)])
-        context = (url, hdrs)
-        if context in self.authContext:
-            self.authContext.clear()
-            result = HTTPError(
-                req.get_full_url(), code, msg, headers, fp)
-            result.status = code
-            return result
-        self.authContext.add(context)
-        return self.http_error_auth_reqed('www-authenticate',
-                                          url, req, headers)
-
+import httplib2
 
 class RESTService(object):
 
@@ -112,7 +43,7 @@
 
         """ Makes a get request to the URL specified."""
 
-        headers = None
+        headers = {}
         if kwargs:
             if 'headers' in kwargs:
                 headers = kwargs['headers']
@@ -125,30 +56,17 @@
 
         self.logger.debug('About to do a GET on:' + url)
 
-        request = RESTRequest(url, method='GET')
+        h = httplib2.Http()
+        h.add_credentials(username, password)
+        headers['User-Agent'] = self.user_agent
 
-        # add a user-agent
-        request.add_header('User-Agent', self.user_agent)
-        if headers:
-            for k, v in headers.items():
-                self.logger.debug('Adding header:%s:%s' % (k, v))
-                request.add_header(k, v)
-
-        # create a password manager
-        passwordManager = HTTPPasswordMgrWithDefaultRealm()
-        passwordManager.add_password(None, url, username, password)
-
-        opener = build_opener(SmartRedirectHandler(),
-                              DefaultErrorHandler(),
-                              ContextualBasicAuthHandler(passwordManager))
-
-        return opener.open(request)
+        return h.request(url, method='GET', headers=headers)
 
     def delete(self, url, username=None, password=None, **kwargs):
 
         """ Makes a delete request to the URL specified. """
 
-        headers = None
+        headers = {}
         if kwargs:
             if 'headers' in kwargs:
                 headers = kwargs['headers']
@@ -161,30 +79,11 @@
 
         self.logger.debug('About to do a DELETE on:' + url)
 
-        request = RESTRequest(url, method='DELETE')
+        h = httplib2.Http()
+        h.add_credentials(username, password)
+        headers['User-Agent'] = self.user_agent
 
-        # add a user-agent
-        request.add_header('User-Agent', self.user_agent)
-        if headers:
-            for k, v in headers.items():
-                self.logger.debug('Adding header:%s:%s' % (k, v))
-                request.add_header(k, v)
-
-        # create a password manager
-        passwordManager = HTTPPasswordMgrWithDefaultRealm()
-        passwordManager.add_password(None, url, username, password)
-
-        opener = build_opener(SmartRedirectHandler(),
-                              DefaultErrorHandler(),
-                              ContextualBasicAuthHandler(passwordManager))
-
-        #try:
-        #    opener.open(request)
-        #except urllib2.HTTPError, e:
-        #    if e.code is not 204:
-        #        raise e
-        #return None
-        return opener.open(request)
+        return h.request(url, method='DELETE', headers=headers)
 
     def put(self,
             url,
@@ -200,7 +99,7 @@
         specified content type.
         """
 
-        headers = None
+        headers = {}
         if kwargs:
             if 'headers' in kwargs:
                 headers = kwargs['headers']
@@ -213,27 +112,12 @@
 
         self.logger.debug('About to do a PUT on:' + url)
 
-        request = RESTRequest(url, payload, method='PUT')
-
-        # set the content type header
-        request.add_header('Content-Type', contentType)
-
-        # add a user-agent
-        request.add_header('User-Agent', self.user_agent)
-        if headers:
-            for k, v in headers.items():
-                self.logger.debug('Adding header:%s:%s' % (k, v))
-                request.add_header(k, v)
-
-        # create a password manager
-        passwordManager = HTTPPasswordMgrWithDefaultRealm()
-        passwordManager.add_password(None, url, username, password)
-
-        opener = build_opener(SmartRedirectHandler(),
-                              DefaultErrorHandler(),
-                              ContextualBasicAuthHandler(passwordManager))
-
-        return opener.open(request)
+        h = httplib2.Http()
+        h.add_credentials(username, password)
+        headers['User-Agent'] = self.user_agent
+        if contentType != None:
+            headers['Content-Type'] = contentType
+        return h.request(url, body=payload, method='PUT', headers=headers)
 
     def post(self,
              url,
@@ -249,7 +133,7 @@
         specified content type.
         """
 
-        headers = None
+        headers = {}
         if kwargs:
             if 'headers' in kwargs:
                 headers = kwargs['headers']
@@ -262,47 +146,9 @@
 
         self.logger.debug('About to do a POST on:' + url)
 
-        request = RESTRequest(url, payload, method='POST')
-
-        # set the content type header
-        request.add_header('Content-Type', contentType)
-
-        # add a user-agent
-        request.add_header('User-Agent', self.user_agent)
-        if headers:
-            for k, v in headers.items():
-                self.logger.debug('Adding header:%s:%s' % (k, v))
-                request.add_header(k, v)
-
-        # create a password manager
-        passwordManager = HTTPPasswordMgrWithDefaultRealm()
-        passwordManager.add_password(None, url, username, password)
-
-        opener = build_opener(SmartRedirectHandler(),
-                              DefaultErrorHandler(),
-                              ContextualBasicAuthHandler(passwordManager))
-
-        try:
-            return opener.open(request)
-        except HTTPError, e:
-            if e.code is not 201:
-                return e
-            else:
-                return e.read()
-
-
-class RESTRequest(Request):
-
-    """
-    Overrides urllib's request default behavior
-    """
-
-    def __init__(self, *args, **kwargs):
-        """ Constructor """
-        self._method = kwargs.pop('method', 'GET')
-        assert self._method in ['GET', 'POST', 'PUT', 'DELETE']
-        Request.__init__(self, *args, **kwargs)
-
-    def get_method(self):
-        """ Override the get method """
-        return self._method
+        h = httplib2.Http()
+        h.add_credentials(username, password)
+        headers['User-Agent'] = self.user_agent
+        if contentType != None:
+            headers['Content-Type'] = contentType
+        return h.request(url, body=payload, method='POST', headers=headers)
diff --git a/src/tests/settings.py b/src/tests/settings.py
index c70d009..fe79a5f 100644
--- a/src/tests/settings.py
+++ b/src/tests/settings.py
@@ -48,7 +48,7 @@
 # For repositories that support setting an ACL, the name of an existing
 # principal ID to add to the ACL of a test object. Some repositories care
 # if this ID doesn't exist. Some repositories don't.
-TEST_PRINCIPAL_ID = 'tuser1'
+TEST_PRINCIPAL_ID = 'anyone'
 # For repositories that may index test content asynchronously, the number of
 # times a query is retried before giving up.
 MAX_FULL_TEXT_TRIES = 10