I really enjoy how simple it is to use xmlrpclib over other APIs such as REST. I also understand that it's insecure, as it does not support authentication as REST APIs do. Although there should technically be a way to implement an authentication layer over XML-RPC. The SimpleXMLRPCServer class which ships in Python's standard library doesn't natively support SSL socket encryption. For private XML-RPC Services, which I do not want otherwise exposed, especially over plain text, SSL encryption is required. I also prefer a stand-alone server over piggybacking on an SSL enabled web-server. This enables me to choose a random port, and create services which can otherwise monitor the web service remotely. How can you monitor a web service using a web service, if the web service is having trouble... Also since the xmlrpclib module ships in the Python standard library, and supports SSL/HTTPS servers, it makes it a great light-weight protocol to use anywhere Python can be installed. I have Python installed on my Symbian smartphone, and both my Android phone and tablet. Running a quick script from my home screen yields quick results from my XML-RPC service. Anyways, enough of my rambling and justifications for using XML-RPC over a more popular protocol... Here's the class you came here to steal from my blog:
from SocketServer import TCPServer import ssl from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCDispatcher, SimpleXMLRPCRequestHandler try: import fcntl except ImportError: fcntl = None CERT_FILE = '/home/kveroneau/cert.pem' class SSLServer(TCPServer): def get_request(self): newsocket, fromaddr = self.socket.accept() connstream = ssl.wrap_socket(newsocket, server_side=True, certfile=CERT_FILE, keyfile=CERT_FILE, ssl_version=ssl.PROTOCOL_SSLv23) return (connstream, fromaddr) class SimpleXMLRPCServer(SSLServer, SimpleXMLRPCDispatcher): """Simple XML-RPC server. Simple XML-RPC server that allows functions and a single instance to be installed to handle requests. The default implementation attempts to dispatch XML-RPC calls to the functions or instance installed in the server. Override the _dispatch method inhereted from SimpleXMLRPCDispatcher to change this behavior. """ allow_reuse_address = True # Warning: this is for debugging purposes only! Never set this to True in # production code, as will be sending out sensitive information (exception # and stack trace details) when exceptions are raised inside # SimpleXMLRPCRequestHandler.do_POST _send_traceback_header = False def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, logRequests=True, allow_none=False, encoding=None, bind_and_activate=True): self.logRequests = logRequests SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding) SSLServer.__init__(self, addr, requestHandler, bind_and_activate) # [Bug #1222790] If possible, set close-on-exec flag; if a # method spawns a subprocess, the subprocess shouldn't have # the listening socket open. if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'): flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD) flags |= fcntl.FD_CLOEXEC fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags) if __name__ == '__main__': print 'Running XML-RPC server on port 8000' server = SimpleXMLRPCServer(("localhost", 8000)) server.register_function(pow) server.register_function(lambda x,y: x+y, 'add') server.serve_forever()
There must be a more elegant way of doing this, but I was unsuccessful with creating a Mixin that could simply overwrite TCPServer.get_request(). If you know of a better way of implementing this code, please send it my way, and I will update this post and credit you for your assistance. To generate the PEM file mentioned above, use this command-line:
kveroneau@sys1:~$ openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout cert.pem Generating a 1024 bit RSA private key ...++++++ ..................................++++++ writing new private key to 'cert.pem' ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:CA State or Province Name (full name) [Some-State]:Manitoba Locality Name (eg, city) :Winnipeg Organization Name (eg, company) [Internet Widgits Pty Ltd]:Veroneau.net Organizational Unit Name (eg, section) :I.T. Common Name (eg, YOUR name) :Kevin Email Address :***HIDDEN***
A bonus of this, is that you can use SSLServer class to replace any TCPServer class and add SSL to any existing program that uses TCPServer. For non-light weight programs, consider using Twisted, it is a much more featureful Python package for creating and deploying networked applications.