Added: incubator/chemistry/trunk/cmislib/src/cmislib/net.py URL: http://svn.apache.org/viewvc/incubator/chemistry/trunk/cmislib/src/cmislib/net.py?rev=912218&view=auto ============================================================================== --- incubator/chemistry/trunk/cmislib/src/cmislib/net.py (added) +++ incubator/chemistry/trunk/cmislib/src/cmislib/net.py Sat Feb 20 20:32:57 2010 @@ -0,0 +1,228 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Authors: +# Jeff Potts, Optaros +# +''' +Module that knows how to connect to the AtomPub Binding of a CMIS repo +''' + +from urllib import urlencode +from urllib2 import HTTPBasicAuthHandler, \ + HTTPPasswordMgrWithDefaultRealm, \ + HTTPRedirectHandler, \ + HTTPDefaultErrorHandler, \ + HTTPError, \ + Request, \ + build_opener + + +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 RESTService(object): + + """ + Generic service for interacting with an HTTP end point. Sets headers + such as the USER_AGENT and builds the basic auth handler. + """ + + def __init__(self): + self.user_agent = 'cmislib/%s +http://code.google.com/p/cmislib/' + + def get(self, + url, + username=None, + password=None, + **kwargs): + + """ Makes a get request to the URL specified.""" + + if kwargs: + if url.find('?') >= 0: + url = url + '&' + urlencode(kwargs) + else: + url = url + '?' + urlencode(kwargs) + + request = RESTRequest(url, method='GET') + + # add a user-agent + request.add_header('User-Agent', self.user_agent) + + # create a password manager + passwordManager = HTTPPasswordMgrWithDefaultRealm() + passwordManager.add_password(None, url, username, password) + + opener = build_opener(SmartRedirectHandler(), + DefaultErrorHandler(), + HTTPBasicAuthHandler(passwordManager)) + + return opener.open(request) + + def delete(self, url, username=None, password=None, **kwargs): + + """ Makes a delete request to the URL specified. """ + + if kwargs: + if url.find('?') >= 0: + url = url + '&' + urlencode(kwargs) + else: + url = url + '?' + urlencode(kwargs) + + request = RESTRequest(url, method='DELETE') + + # add a user-agent + request.add_header('User-Agent', self.user_agent) + + # create a password manager + passwordManager = HTTPPasswordMgrWithDefaultRealm() + passwordManager.add_password(None, url, username, password) + + opener = build_opener(SmartRedirectHandler(), + DefaultErrorHandler(), + HTTPBasicAuthHandler(passwordManager)) + + #try: + # opener.open(request) + #except urllib2.HTTPError, e: + # if e.code is not 204: + # raise e + #return None + return opener.open(request) + + def put(self, + url, + payload, + contentType, + username=None, + password=None, + **kwargs): + + """ + Makes a PUT request to the URL specified and includes the payload + that gets passed in. The content type header gets set to the + specified content type. + """ + + if kwargs: + if url.find('?') >= 0: + url = url + '&' + urlencode(kwargs) + else: + url = url + '?' + urlencode(kwargs) + + 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) + # create a password manager + passwordManager = HTTPPasswordMgrWithDefaultRealm() + passwordManager.add_password(None, url, username, password) + + opener = build_opener(SmartRedirectHandler(), + DefaultErrorHandler(), + HTTPBasicAuthHandler(passwordManager)) + + return opener.open(request) + + def post(self, + url, + payload, + contentType, + username=None, + password=None, + **kwargs): + + """ + Makes a POST request to the URL specified and posts the payload + that gets passed in. The content type header gets set to the + specified content type. + """ + + if kwargs: + if url.find('?') >= 0: + url = url + '&' + urlencode(kwargs) + else: + url = url + '?' + urlencode(kwargs) + + 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) + + # create a password manager + passwordManager = HTTPPasswordMgrWithDefaultRealm() + passwordManager.add_password(None, url, username, password) + + opener = build_opener(SmartRedirectHandler(), + DefaultErrorHandler(), + HTTPBasicAuthHandler(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
