| """Simple XML-RPC Server. | 
 |  | 
 | This module can be used to create simple XML-RPC servers | 
 | by creating a server and either installing functions, a | 
 | class instance, or by extending the SimpleXMLRPCServer | 
 | class. | 
 |  | 
 | It can also be used to handle XML-RPC requests in a CGI | 
 | environment using CGIXMLRPCRequestHandler. | 
 |  | 
 | A list of possible usage patterns follows: | 
 |  | 
 | 1. Install functions: | 
 |  | 
 | server = SimpleXMLRPCServer(("localhost", 8000)) | 
 | server.register_function(pow) | 
 | server.register_function(lambda x,y: x+y, 'add') | 
 | server.serve_forever() | 
 |  | 
 | 2. Install an instance: | 
 |  | 
 | class MyFuncs: | 
 |     def __init__(self): | 
 |         # make all of the string functions available through | 
 |         # string.func_name | 
 |         import string | 
 |         self.string = string | 
 |     def _listMethods(self): | 
 |         # implement this method so that system.listMethods | 
 |         # knows to advertise the strings methods | 
 |         return list_public_methods(self) + \ | 
 |                 ['string.' + method for method in list_public_methods(self.string)] | 
 |     def pow(self, x, y): return pow(x, y) | 
 |     def add(self, x, y) : return x + y | 
 |  | 
 | server = SimpleXMLRPCServer(("localhost", 8000)) | 
 | server.register_introspection_functions() | 
 | server.register_instance(MyFuncs()) | 
 | server.serve_forever() | 
 |  | 
 | 3. Install an instance with custom dispatch method: | 
 |  | 
 | class Math: | 
 |     def _listMethods(self): | 
 |         # this method must be present for system.listMethods | 
 |         # to work | 
 |         return ['add', 'pow'] | 
 |     def _methodHelp(self, method): | 
 |         # this method must be present for system.methodHelp | 
 |         # to work | 
 |         if method == 'add': | 
 |             return "add(2,3) => 5" | 
 |         elif method == 'pow': | 
 |             return "pow(x, y[, z]) => number" | 
 |         else: | 
 |             # By convention, return empty | 
 |             # string if no help is available | 
 |             return "" | 
 |     def _dispatch(self, method, params): | 
 |         if method == 'pow': | 
 |             return pow(*params) | 
 |         elif method == 'add': | 
 |             return params[0] + params[1] | 
 |         else: | 
 |             raise 'bad method' | 
 |  | 
 | server = SimpleXMLRPCServer(("localhost", 8000)) | 
 | server.register_introspection_functions() | 
 | server.register_instance(Math()) | 
 | server.serve_forever() | 
 |  | 
 | 4. Subclass SimpleXMLRPCServer: | 
 |  | 
 | class MathServer(SimpleXMLRPCServer): | 
 |     def _dispatch(self, method, params): | 
 |         try: | 
 |             # We are forcing the 'export_' prefix on methods that are | 
 |             # callable through XML-RPC to prevent potential security | 
 |             # problems | 
 |             func = getattr(self, 'export_' + method) | 
 |         except AttributeError: | 
 |             raise Exception('method "%s" is not supported' % method) | 
 |         else: | 
 |             return func(*params) | 
 |  | 
 |     def export_add(self, x, y): | 
 |         return x + y | 
 |  | 
 | server = MathServer(("localhost", 8000)) | 
 | server.serve_forever() | 
 |  | 
 | 5. CGI script: | 
 |  | 
 | server = CGIXMLRPCRequestHandler() | 
 | server.register_function(pow) | 
 | server.handle_request() | 
 | """ | 
 |  | 
 | # Written by Brian Quinlan (brian@sweetapp.com). | 
 | # Based on code written by Fredrik Lundh. | 
 |  | 
 | import xmlrpclib | 
 | from xmlrpclib import Fault | 
 | import SocketServer | 
 | import BaseHTTPServer | 
 | import sys | 
 | import os | 
 | import traceback | 
 | import re | 
 | try: | 
 |     import fcntl | 
 | except ImportError: | 
 |     fcntl = None | 
 |  | 
 | def resolve_dotted_attribute(obj, attr, allow_dotted_names=True): | 
 |     """resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d | 
 |  | 
 |     Resolves a dotted attribute name to an object.  Raises | 
 |     an AttributeError if any attribute in the chain starts with a '_'. | 
 |  | 
 |     If the optional allow_dotted_names argument is false, dots are not | 
 |     supported and this function operates similar to getattr(obj, attr). | 
 |     """ | 
 |  | 
 |     if allow_dotted_names: | 
 |         attrs = attr.split('.') | 
 |     else: | 
 |         attrs = [attr] | 
 |  | 
 |     for i in attrs: | 
 |         if i.startswith('_'): | 
 |             raise AttributeError( | 
 |                 'attempt to access private attribute "%s"' % i | 
 |                 ) | 
 |         else: | 
 |             obj = getattr(obj,i) | 
 |     return obj | 
 |  | 
 | def list_public_methods(obj): | 
 |     """Returns a list of attribute strings, found in the specified | 
 |     object, which represent callable attributes""" | 
 |  | 
 |     return [member for member in dir(obj) | 
 |                 if not member.startswith('_') and | 
 |                     hasattr(getattr(obj, member), '__call__')] | 
 |  | 
 | def remove_duplicates(lst): | 
 |     """remove_duplicates([2,2,2,1,3,3]) => [3,1,2] | 
 |  | 
 |     Returns a copy of a list without duplicates. Every list | 
 |     item must be hashable and the order of the items in the | 
 |     resulting list is not defined. | 
 |     """ | 
 |     u = {} | 
 |     for x in lst: | 
 |         u[x] = 1 | 
 |  | 
 |     return u.keys() | 
 |  | 
 | class SimpleXMLRPCDispatcher: | 
 |     """Mix-in class that dispatches XML-RPC requests. | 
 |  | 
 |     This class is used to register XML-RPC method handlers | 
 |     and then to dispatch them. This class doesn't need to be | 
 |     instanced directly when used by SimpleXMLRPCServer but it | 
 |     can be instanced when used by the MultiPathXMLRPCServer | 
 |     """ | 
 |  | 
 |     def __init__(self, allow_none=False, encoding=None): | 
 |         self.funcs = {} | 
 |         self.instance = None | 
 |         self.allow_none = allow_none | 
 |         self.encoding = encoding | 
 |  | 
 |     def register_instance(self, instance, allow_dotted_names=False): | 
 |         """Registers an instance to respond to XML-RPC requests. | 
 |  | 
 |         Only one instance can be installed at a time. | 
 |  | 
 |         If the registered instance has a _dispatch method then that | 
 |         method will be called with the name of the XML-RPC method and | 
 |         its parameters as a tuple | 
 |         e.g. instance._dispatch('add',(2,3)) | 
 |  | 
 |         If the registered instance does not have a _dispatch method | 
 |         then the instance will be searched to find a matching method | 
 |         and, if found, will be called. Methods beginning with an '_' | 
 |         are considered private and will not be called by | 
 |         SimpleXMLRPCServer. | 
 |  | 
 |         If a registered function matches a XML-RPC request, then it | 
 |         will be called instead of the registered instance. | 
 |  | 
 |         If the optional allow_dotted_names argument is true and the | 
 |         instance does not have a _dispatch method, method names | 
 |         containing dots are supported and resolved, as long as none of | 
 |         the name segments start with an '_'. | 
 |  | 
 |             *** SECURITY WARNING: *** | 
 |  | 
 |             Enabling the allow_dotted_names options allows intruders | 
 |             to access your module's global variables and may allow | 
 |             intruders to execute arbitrary code on your machine.  Only | 
 |             use this option on a secure, closed network. | 
 |  | 
 |         """ | 
 |  | 
 |         self.instance = instance | 
 |         self.allow_dotted_names = allow_dotted_names | 
 |  | 
 |     def register_function(self, function, name = None): | 
 |         """Registers a function to respond to XML-RPC requests. | 
 |  | 
 |         The optional name argument can be used to set a Unicode name | 
 |         for the function. | 
 |         """ | 
 |  | 
 |         if name is None: | 
 |             name = function.__name__ | 
 |         self.funcs[name] = function | 
 |  | 
 |     def register_introspection_functions(self): | 
 |         """Registers the XML-RPC introspection methods in the system | 
 |         namespace. | 
 |  | 
 |         see http://xmlrpc.usefulinc.com/doc/reserved.html | 
 |         """ | 
 |  | 
 |         self.funcs.update({'system.listMethods' : self.system_listMethods, | 
 |                       'system.methodSignature' : self.system_methodSignature, | 
 |                       'system.methodHelp' : self.system_methodHelp}) | 
 |  | 
 |     def register_multicall_functions(self): | 
 |         """Registers the XML-RPC multicall method in the system | 
 |         namespace. | 
 |  | 
 |         see http://www.xmlrpc.com/discuss/msgReader$1208""" | 
 |  | 
 |         self.funcs.update({'system.multicall' : self.system_multicall}) | 
 |  | 
 |     def _marshaled_dispatch(self, data, dispatch_method = None, path = None): | 
 |         """Dispatches an XML-RPC method from marshalled (XML) data. | 
 |  | 
 |         XML-RPC methods are dispatched from the marshalled (XML) data | 
 |         using the _dispatch method and the result is returned as | 
 |         marshalled data. For backwards compatibility, a dispatch | 
 |         function can be provided as an argument (see comment in | 
 |         SimpleXMLRPCRequestHandler.do_POST) but overriding the | 
 |         existing method through subclassing is the prefered means | 
 |         of changing method dispatch behavior. | 
 |         """ | 
 |  | 
 |         try: | 
 |             params, method = xmlrpclib.loads(data) | 
 |  | 
 |             # generate response | 
 |             if dispatch_method is not None: | 
 |                 response = dispatch_method(method, params) | 
 |             else: | 
 |                 response = self._dispatch(method, params) | 
 |             # wrap response in a singleton tuple | 
 |             response = (response,) | 
 |             response = xmlrpclib.dumps(response, methodresponse=1, | 
 |                                        allow_none=self.allow_none, encoding=self.encoding) | 
 |         except Fault, fault: | 
 |             response = xmlrpclib.dumps(fault, allow_none=self.allow_none, | 
 |                                        encoding=self.encoding) | 
 |         except: | 
 |             # report exception back to server | 
 |             exc_type, exc_value, exc_tb = sys.exc_info() | 
 |             response = xmlrpclib.dumps( | 
 |                 xmlrpclib.Fault(1, "%s:%s" % (exc_type, exc_value)), | 
 |                 encoding=self.encoding, allow_none=self.allow_none, | 
 |                 ) | 
 |  | 
 |         return response | 
 |  | 
 |     def system_listMethods(self): | 
 |         """system.listMethods() => ['add', 'subtract', 'multiple'] | 
 |  | 
 |         Returns a list of the methods supported by the server.""" | 
 |  | 
 |         methods = self.funcs.keys() | 
 |         if self.instance is not None: | 
 |             # Instance can implement _listMethod to return a list of | 
 |             # methods | 
 |             if hasattr(self.instance, '_listMethods'): | 
 |                 methods = remove_duplicates( | 
 |                         methods + self.instance._listMethods() | 
 |                     ) | 
 |             # if the instance has a _dispatch method then we | 
 |             # don't have enough information to provide a list | 
 |             # of methods | 
 |             elif not hasattr(self.instance, '_dispatch'): | 
 |                 methods = remove_duplicates( | 
 |                         methods + list_public_methods(self.instance) | 
 |                     ) | 
 |         methods.sort() | 
 |         return methods | 
 |  | 
 |     def system_methodSignature(self, method_name): | 
 |         """system.methodSignature('add') => [double, int, int] | 
 |  | 
 |         Returns a list describing the signature of the method. In the | 
 |         above example, the add method takes two integers as arguments | 
 |         and returns a double result. | 
 |  | 
 |         This server does NOT support system.methodSignature.""" | 
 |  | 
 |         # See http://xmlrpc.usefulinc.com/doc/sysmethodsig.html | 
 |  | 
 |         return 'signatures not supported' | 
 |  | 
 |     def system_methodHelp(self, method_name): | 
 |         """system.methodHelp('add') => "Adds two integers together" | 
 |  | 
 |         Returns a string containing documentation for the specified method.""" | 
 |  | 
 |         method = None | 
 |         if method_name in self.funcs: | 
 |             method = self.funcs[method_name] | 
 |         elif self.instance is not None: | 
 |             # Instance can implement _methodHelp to return help for a method | 
 |             if hasattr(self.instance, '_methodHelp'): | 
 |                 return self.instance._methodHelp(method_name) | 
 |             # if the instance has a _dispatch method then we | 
 |             # don't have enough information to provide help | 
 |             elif not hasattr(self.instance, '_dispatch'): | 
 |                 try: | 
 |                     method = resolve_dotted_attribute( | 
 |                                 self.instance, | 
 |                                 method_name, | 
 |                                 self.allow_dotted_names | 
 |                                 ) | 
 |                 except AttributeError: | 
 |                     pass | 
 |  | 
 |         # Note that we aren't checking that the method actually | 
 |         # be a callable object of some kind | 
 |         if method is None: | 
 |             return "" | 
 |         else: | 
 |             import pydoc | 
 |             return pydoc.getdoc(method) | 
 |  | 
 |     def system_multicall(self, call_list): | 
 |         """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \ | 
 | [[4], ...] | 
 |  | 
 |         Allows the caller to package multiple XML-RPC calls into a single | 
 |         request. | 
 |  | 
 |         See http://www.xmlrpc.com/discuss/msgReader$1208 | 
 |         """ | 
 |  | 
 |         results = [] | 
 |         for call in call_list: | 
 |             method_name = call['methodName'] | 
 |             params = call['params'] | 
 |  | 
 |             try: | 
 |                 # XXX A marshalling error in any response will fail the entire | 
 |                 # multicall. If someone cares they should fix this. | 
 |                 results.append([self._dispatch(method_name, params)]) | 
 |             except Fault, fault: | 
 |                 results.append( | 
 |                     {'faultCode' : fault.faultCode, | 
 |                      'faultString' : fault.faultString} | 
 |                     ) | 
 |             except: | 
 |                 exc_type, exc_value, exc_tb = sys.exc_info() | 
 |                 results.append( | 
 |                     {'faultCode' : 1, | 
 |                      'faultString' : "%s:%s" % (exc_type, exc_value)} | 
 |                     ) | 
 |         return results | 
 |  | 
 |     def _dispatch(self, method, params): | 
 |         """Dispatches the XML-RPC method. | 
 |  | 
 |         XML-RPC calls are forwarded to a registered function that | 
 |         matches the called XML-RPC method name. If no such function | 
 |         exists then the call is forwarded to the registered instance, | 
 |         if available. | 
 |  | 
 |         If the registered instance has a _dispatch method then that | 
 |         method will be called with the name of the XML-RPC method and | 
 |         its parameters as a tuple | 
 |         e.g. instance._dispatch('add',(2,3)) | 
 |  | 
 |         If the registered instance does not have a _dispatch method | 
 |         then the instance will be searched to find a matching method | 
 |         and, if found, will be called. | 
 |  | 
 |         Methods beginning with an '_' are considered private and will | 
 |         not be called. | 
 |         """ | 
 |  | 
 |         func = None | 
 |         try: | 
 |             # check to see if a matching function has been registered | 
 |             func = self.funcs[method] | 
 |         except KeyError: | 
 |             if self.instance is not None: | 
 |                 # check for a _dispatch method | 
 |                 if hasattr(self.instance, '_dispatch'): | 
 |                     return self.instance._dispatch(method, params) | 
 |                 else: | 
 |                     # call instance method directly | 
 |                     try: | 
 |                         func = resolve_dotted_attribute( | 
 |                             self.instance, | 
 |                             method, | 
 |                             self.allow_dotted_names | 
 |                             ) | 
 |                     except AttributeError: | 
 |                         pass | 
 |  | 
 |         if func is not None: | 
 |             return func(*params) | 
 |         else: | 
 |             raise Exception('method "%s" is not supported' % method) | 
 |  | 
 | class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | 
 |     """Simple XML-RPC request handler class. | 
 |  | 
 |     Handles all HTTP POST requests and attempts to decode them as | 
 |     XML-RPC requests. | 
 |     """ | 
 |  | 
 |     # Class attribute listing the accessible path components; | 
 |     # paths not on this list will result in a 404 error. | 
 |     rpc_paths = ('/', '/RPC2') | 
 |  | 
 |     #if not None, encode responses larger than this, if possible | 
 |     encode_threshold = 1400 #a common MTU | 
 |  | 
 |     #Override form StreamRequestHandler: full buffering of output | 
 |     #and no Nagle. | 
 |     wbufsize = -1 | 
 |     disable_nagle_algorithm = True | 
 |  | 
 |     # a re to match a gzip Accept-Encoding | 
 |     aepattern = re.compile(r""" | 
 |                             \s* ([^\s;]+) \s*            #content-coding | 
 |                             (;\s* q \s*=\s* ([0-9\.]+))? #q | 
 |                             """, re.VERBOSE | re.IGNORECASE) | 
 |  | 
 |     def accept_encodings(self): | 
 |         r = {} | 
 |         ae = self.headers.get("Accept-Encoding", "") | 
 |         for e in ae.split(","): | 
 |             match = self.aepattern.match(e) | 
 |             if match: | 
 |                 v = match.group(3) | 
 |                 v = float(v) if v else 1.0 | 
 |                 r[match.group(1)] = v | 
 |         return r | 
 |  | 
 |     def is_rpc_path_valid(self): | 
 |         if self.rpc_paths: | 
 |             return self.path in self.rpc_paths | 
 |         else: | 
 |             # If .rpc_paths is empty, just assume all paths are legal | 
 |             return True | 
 |  | 
 |     def do_POST(self): | 
 |         """Handles the HTTP POST request. | 
 |  | 
 |         Attempts to interpret all HTTP POST requests as XML-RPC calls, | 
 |         which are forwarded to the server's _dispatch method for handling. | 
 |         """ | 
 |  | 
 |         # Check that the path is legal | 
 |         if not self.is_rpc_path_valid(): | 
 |             self.report_404() | 
 |             return | 
 |  | 
 |         try: | 
 |             # Get arguments by reading body of request. | 
 |             # We read this in chunks to avoid straining | 
 |             # socket.read(); around the 10 or 15Mb mark, some platforms | 
 |             # begin to have problems (bug #792570). | 
 |             max_chunk_size = 10*1024*1024 | 
 |             size_remaining = int(self.headers["content-length"]) | 
 |             L = [] | 
 |             while size_remaining: | 
 |                 chunk_size = min(size_remaining, max_chunk_size) | 
 |                 L.append(self.rfile.read(chunk_size)) | 
 |                 size_remaining -= len(L[-1]) | 
 |             data = ''.join(L) | 
 |  | 
 |             data = self.decode_request_content(data) | 
 |             if data is None: | 
 |                 return #response has been sent | 
 |  | 
 |             # In previous versions of SimpleXMLRPCServer, _dispatch | 
 |             # could be overridden in this class, instead of in | 
 |             # SimpleXMLRPCDispatcher. To maintain backwards compatibility, | 
 |             # check to see if a subclass implements _dispatch and dispatch | 
 |             # using that method if present. | 
 |             response = self.server._marshaled_dispatch( | 
 |                     data, getattr(self, '_dispatch', None), self.path | 
 |                 ) | 
 |         except Exception, e: # This should only happen if the module is buggy | 
 |             # internal error, report as HTTP server error | 
 |             self.send_response(500) | 
 |  | 
 |             # Send information about the exception if requested | 
 |             if hasattr(self.server, '_send_traceback_header') and \ | 
 |                     self.server._send_traceback_header: | 
 |                 self.send_header("X-exception", str(e)) | 
 |                 self.send_header("X-traceback", traceback.format_exc()) | 
 |  | 
 |             self.send_header("Content-length", "0") | 
 |             self.end_headers() | 
 |         else: | 
 |             # got a valid XML RPC response | 
 |             self.send_response(200) | 
 |             self.send_header("Content-type", "text/xml") | 
 |             if self.encode_threshold is not None: | 
 |                 if len(response) > self.encode_threshold: | 
 |                     q = self.accept_encodings().get("gzip", 0) | 
 |                     if q: | 
 |                         try: | 
 |                             response = xmlrpclib.gzip_encode(response) | 
 |                             self.send_header("Content-Encoding", "gzip") | 
 |                         except NotImplementedError: | 
 |                             pass | 
 |             self.send_header("Content-length", str(len(response))) | 
 |             self.end_headers() | 
 |             self.wfile.write(response) | 
 |  | 
 |     def decode_request_content(self, data): | 
 |         #support gzip encoding of request | 
 |         encoding = self.headers.get("content-encoding", "identity").lower() | 
 |         if encoding == "identity": | 
 |             return data | 
 |         if encoding == "gzip": | 
 |             try: | 
 |                 return xmlrpclib.gzip_decode(data) | 
 |             except NotImplementedError: | 
 |                 self.send_response(501, "encoding %r not supported" % encoding) | 
 |             except ValueError: | 
 |                 self.send_response(400, "error decoding gzip content") | 
 |         else: | 
 |             self.send_response(501, "encoding %r not supported" % encoding) | 
 |         self.send_header("Content-length", "0") | 
 |         self.end_headers() | 
 |  | 
 |     def report_404 (self): | 
 |             # Report a 404 error | 
 |         self.send_response(404) | 
 |         response = 'No such page' | 
 |         self.send_header("Content-type", "text/plain") | 
 |         self.send_header("Content-length", str(len(response))) | 
 |         self.end_headers() | 
 |         self.wfile.write(response) | 
 |  | 
 |     def log_request(self, code='-', size='-'): | 
 |         """Selectively log an accepted request.""" | 
 |  | 
 |         if self.server.logRequests: | 
 |             BaseHTTPServer.BaseHTTPRequestHandler.log_request(self, code, size) | 
 |  | 
 | class SimpleXMLRPCServer(SocketServer.TCPServer, | 
 |                          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) | 
 |         SocketServer.TCPServer.__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) | 
 |  | 
 | class MultiPathXMLRPCServer(SimpleXMLRPCServer): | 
 |     """Multipath XML-RPC Server | 
 |     This specialization of SimpleXMLRPCServer allows the user to create | 
 |     multiple Dispatcher instances and assign them to different | 
 |     HTTP request paths.  This makes it possible to run two or more | 
 |     'virtual XML-RPC servers' at the same port. | 
 |     Make sure that the requestHandler accepts the paths in question. | 
 |     """ | 
 |     def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, | 
 |                  logRequests=True, allow_none=False, encoding=None, bind_and_activate=True): | 
 |  | 
 |         SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, allow_none, | 
 |                                     encoding, bind_and_activate) | 
 |         self.dispatchers = {} | 
 |         self.allow_none = allow_none | 
 |         self.encoding = encoding | 
 |  | 
 |     def add_dispatcher(self, path, dispatcher): | 
 |         self.dispatchers[path] = dispatcher | 
 |         return dispatcher | 
 |  | 
 |     def get_dispatcher(self, path): | 
 |         return self.dispatchers[path] | 
 |  | 
 |     def _marshaled_dispatch(self, data, dispatch_method = None, path = None): | 
 |         try: | 
 |             response = self.dispatchers[path]._marshaled_dispatch( | 
 |                data, dispatch_method, path) | 
 |         except: | 
 |             # report low level exception back to server | 
 |             # (each dispatcher should have handled their own | 
 |             # exceptions) | 
 |             exc_type, exc_value = sys.exc_info()[:2] | 
 |             response = xmlrpclib.dumps( | 
 |                 xmlrpclib.Fault(1, "%s:%s" % (exc_type, exc_value)), | 
 |                 encoding=self.encoding, allow_none=self.allow_none) | 
 |         return response | 
 |  | 
 | class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher): | 
 |     """Simple handler for XML-RPC data passed through CGI.""" | 
 |  | 
 |     def __init__(self, allow_none=False, encoding=None): | 
 |         SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding) | 
 |  | 
 |     def handle_xmlrpc(self, request_text): | 
 |         """Handle a single XML-RPC request""" | 
 |  | 
 |         response = self._marshaled_dispatch(request_text) | 
 |  | 
 |         print 'Content-Type: text/xml' | 
 |         print 'Content-Length: %d' % len(response) | 
 |         print | 
 |         sys.stdout.write(response) | 
 |  | 
 |     def handle_get(self): | 
 |         """Handle a single HTTP GET request. | 
 |  | 
 |         Default implementation indicates an error because | 
 |         XML-RPC uses the POST method. | 
 |         """ | 
 |  | 
 |         code = 400 | 
 |         message, explain = \ | 
 |                  BaseHTTPServer.BaseHTTPRequestHandler.responses[code] | 
 |  | 
 |         response = BaseHTTPServer.DEFAULT_ERROR_MESSAGE % \ | 
 |             { | 
 |              'code' : code, | 
 |              'message' : message, | 
 |              'explain' : explain | 
 |             } | 
 |         print 'Status: %d %s' % (code, message) | 
 |         print 'Content-Type: %s' % BaseHTTPServer.DEFAULT_ERROR_CONTENT_TYPE | 
 |         print 'Content-Length: %d' % len(response) | 
 |         print | 
 |         sys.stdout.write(response) | 
 |  | 
 |     def handle_request(self, request_text = None): | 
 |         """Handle a single XML-RPC request passed through a CGI post method. | 
 |  | 
 |         If no XML data is given then it is read from stdin. The resulting | 
 |         XML-RPC response is printed to stdout along with the correct HTTP | 
 |         headers. | 
 |         """ | 
 |  | 
 |         if request_text is None and \ | 
 |             os.environ.get('REQUEST_METHOD', None) == 'GET': | 
 |             self.handle_get() | 
 |         else: | 
 |             # POST data is normally available through stdin | 
 |             try: | 
 |                 length = int(os.environ.get('CONTENT_LENGTH', None)) | 
 |             except (TypeError, ValueError): | 
 |                 length = -1 | 
 |             if request_text is None: | 
 |                 request_text = sys.stdin.read(length) | 
 |  | 
 |             self.handle_xmlrpc(request_text) | 
 |  | 
 | 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() |