*** python/dist/src/Lib/SimpleXMLRPCServer.py Fri Sep 28 21:54:34 2001 --- python-rpc/dist/src/Lib/SimpleXMLRPCServer.py Sat Dec 29 22:00:36 2001 *************** *** 2,10 **** 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 SimpleXMLRPCRequestHandler class. A list of possible usage patterns follows: 1. Install functions: --- 2,13 ---- 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: *************** *** 42,50 **** server.register_instance(Math()) server.serve_forever() ! 4. Subclass SimpleXMLRPCRequestHandler: ! class MathHandler(SimpleXMLRPCRequestHandler): def _dispatch(self, method, params): try: # We are forcing the 'export_' prefix on methods that are --- 45,53 ---- 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 *************** *** 56,131 **** else: return apply(func, params) - def log_message(self, format, *args): - pass # maybe do something fancy like write the messages to a file - def export_add(self, x, y): return x + y ! server = SimpleXMLRPCServer(("localhost", 8000), MathHandler) server.serve_forever() """ # Written by Brian Quinlan (brian@sweetapp.com). # Based on code written by Fredrik Lundh. import xmlrpclib import SocketServer import BaseHTTPServer import sys ! class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): ! """Simple XML-RPC request handler class. ! Handles all HTTP POST requests and attempts to decode them as ! XML-RPC requests. ! XML-RPC requests are dispatched to the _dispatch method, which ! may be overriden by subclasses. The default implementation attempts ! to dispatch XML-RPC calls to the functions or instance installed ! in the server. ! """ ! 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 _dispatch method for handling. """ ! try: ! # get arguments ! data = self.rfile.read(int(self.headers["content-length"])) ! params, method = xmlrpclib.loads(data) ! # generate response ! try: response = self._dispatch(method, params) ! # wrap response in a singleton tuple ! response = (response,) ! except: ! # report exception back to server ! response = xmlrpclib.dumps( ! xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value)) ! ) ! else: ! response = xmlrpclib.dumps(response, methodresponse=1) except: ! # internal error, report as HTTP server error ! self.send_response(500) ! self.end_headers() ! else: ! # got a valid XML RPC response ! self.send_response(200) ! self.send_header("Content-type", "text/xml") ! self.send_header("Content-length", str(len(response))) ! self.end_headers() ! self.wfile.write(response) ! ! # shut down the connection ! self.wfile.flush() ! self.connection.shutdown(1) def _dispatch(self, method, params): """Dispatches the XML-RPC method. --- 59,177 ---- else: return apply(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 ! def _resolve_dotted_attribute(obj, attr): ! """Resolves a dotted attribute name to an object. Raises ! an AttributeError if any attribute in the chain starts with a '_'. ! """ ! for i in attr.split('.'): ! if i.startswith('_'): ! raise AttributeError( ! 'attempt to access private attribute "%s"' % i ! ) ! else: ! obj = getattr(obj,i) ! return obj ! ! 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. There should never be any ! reason to instantiate this class directly. ! """ ! ! def __init__(self): ! self.funcs = {} ! self.instance = None ! ! def register_instance(self, instance): ! """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 ! it's 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. """ ! self.instance = instance ! 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 _marshaled_dispatch(self, data, dispatch_method = 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. ! """ ! ! params, method = xmlrpclib.loads(data) ! ! # generate response ! try: ! 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) ! except Fault, fault: ! response = xmlrpclib.dumps(fault) except: ! # report exception back to server ! response = xmlrpclib.dumps( ! xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value)) ! ) + return response + def _dispatch(self, method, params): """Dispatches the XML-RPC method. *************** *** 144,166 **** and, if found, will be called. Methods beginning with an '_' are considered private and will ! not be called by SimpleXMLRPCServer. """ func = None try: # check to see if a matching function has been registered ! func = self.server.funcs[method] except KeyError: ! if self.server.instance is not None: # check for a _dispatch method ! if hasattr(self.server.instance, '_dispatch'): ! return self.server.instance._dispatch(method, params) else: # call instance method directly try: func = _resolve_dotted_attribute( ! self.server.instance, method ) except AttributeError: --- 190,212 ---- 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 ) except AttributeError: *************** *** 170,247 **** return apply(func, params) else: raise Exception('method "%s" is not supported' % method) ! def log_request(self, code='-', size='-'): ! """Selectively log an accepted request.""" ! if self.server.logRequests: ! BaseHTTPServer.BaseHTTPRequestHandler.log_request(self, code, size) ! def _resolve_dotted_attribute(obj, attr): ! """Resolves a dotted attribute name to an object. Raises ! an AttributeError if any attribute in the chain starts with a '_'. ! """ ! for i in attr.split('.'): ! if i.startswith('_'): ! raise AttributeError( ! 'attempt to access private attribute "%s"' % i ! ) else: ! obj = getattr(obj,i) ! return obj ! class SimpleXMLRPCServer(SocketServer.TCPServer): """Simple XML-RPC server. Simple XML-RPC server that allows functions and a single instance ! to be installed to handle requests. """ def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, logRequests=1): - self.funcs = {} self.logRequests = logRequests ! self.instance = None SocketServer.TCPServer.__init__(self, addr, requestHandler) ! def register_instance(self, instance): ! """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 ! it's 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. ! """ ! ! self.instance = instance ! ! 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 an instance is also registered then it will only be called ! if a matching function is not found. """ ! ! if name is None: ! name = function.__name__ ! self.funcs[name] = function if __name__ == '__main__': server = SimpleXMLRPCServer(("localhost", 8000)) --- 216,314 ---- return apply(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. ! XML-RPC requests are dispatched to the server's _dispatch method, ! which may be overriden by subclasses. ! """ + 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. ! """ ! ! try: ! # get arguments ! data = self.rfile.read(int(self.headers["content-length"])) ! # 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 ! # to that method if present. ! response = self.server._marshaled_dispatch(data, getattr(self, '_dispatch', None)) ! except: # This should only happen if the module is buggy ! # internal error, report as HTTP server error ! self.send_response(500) ! self.end_headers() else: ! # got a valid XML RPC response ! self.send_response(200) ! self.send_header("Content-type", "text/xml") ! self.send_header("Content-length", str(len(response))) ! self.end_headers() ! self.wfile.write(response) ! ! # shut down the connection ! self.wfile.flush() ! self.connection.shutdown(1) ! ! 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. """ def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, logRequests=1): self.logRequests = logRequests ! ! SimpleXMLRPCDispatcher.__init__(self) SocketServer.TCPServer.__init__(self, addr, requestHandler) + ! class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher): ! """Simple handler for XML-RPC data passed through CGI.""" ! ! def __init__(self): ! SimpleXMLRPCDispatcher.__init__(self) ! ! 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: ! # POST data is normally available through stdin ! request_text = sys.stdin.read() ! ! response = self._marshaled_dispatch(request_text) ! ! print 'Content-Type: text/xml' ! print 'Content-Length: %d' % len(response) ! print ! print response if __name__ == '__main__': server = SimpleXMLRPCServer(("localhost", 8000))