Python XML-RPC Client with Cookie Handling and SSL
March 9, 2008
The following is an adaptation of Vaibhav Bhatia's cookie-handling XML-RPC client (text source) with a few changes to implement SSL for HTTPS transport in place of the basic HTTP transport.
This was done specifically for use with CherryPy sessions so that the XML-RPC client (the server proxy object) could be stored in a session variable, giving each user an individual client and thus allowing webservice permissions to be handled on a per-user basis (without having to authenticate and determine permissions on every webservice method invocation). To do this, make sure that you're storing CherryPy sessions in memory, not in a file, and then create the proxy object (sorry for the gigantic class name):
cherrypy.session['proxy'] = \ xmlrpclib.ServerProxy(url, transport=CookieAuthXMLRPCSafeTransport())
xmlrpcclient.py
import os import base64 import xmlrpclib import urllib2 import cookielib class CookieAuthXMLRPCSafeTransport(xmlrpclib.SafeTransport): """ xmlrpclib.Transport that sends HTTPS Authentication""" user_agent = '*py*' credentials = () cookiefile = 'cookies.lwp' def send_basic_auth(self, connection): """Include HTTPS Authentication data in a header""" auth = base64.encodestring("%s:%s"%self.credentials).strip() auth = 'Basic %s' %(auth,) connection.putheader('Authorization',auth) def send_cookie_auth(self, connection): """Include Cookie Authentication data in a header""" cj = cookielib.LWPCookieJar() cj.load(self.cookiefile) for cookie in cj: if cookie.name == 'session_id': uuidstr = cookie.value connection.putheader("Cookie",cookie.name+'='+cookie.value) ## override the send_host hook to also send authentication info def send_host(self, connection, host): xmlrpclib.SafeTransport.send_host(self, connection, host) if os.path.exists(self.cookiefile): self.send_cookie_auth(connection) elif self.credentials != (): self.send_basic_auth(connection) def request(self, host, handler, request_body, verbose=0): # dummy request class for extracting cookies class CookieRequest(urllib2.Request): pass # dummy response class for extracting cookies class CookieResponse: def __init__(self, headers): self.headers = headers def info(self): return self.headers crequest = CookieRequest('https://'+host+'/') # issue XML-RPC request h = self.make_connection(host) if verbose: h.set_debuglevel(1) self.send_request(h, handler, request_body) self.send_host(h, host) self.send_user_agent(h) # creating a cookie jar for my cookies cj = cookielib.LWPCookieJar() self.send_content(h, request_body) errcode, errmsg, headers = h.getreply() cresponse = CookieResponse(headers) cj.extract_cookies(cresponse, crequest) if len(cj) > 0 and self.cookiefile != None: cj.save(self.cookiefile) if errcode != 200: raise xmlrpclib.ProtocolError( host + handler, errcode, errmsg, headers ) self.verbose = verbose return self.parse_response(h.getfile())