*** python\dist\src\Lib\SimpleXMLRPCServer.py Sun Sep 30 13:01:56 2001 --- python-dev\dist\src\Lib\SimpleXMLRPCServer.py Mon Oct 1 00:35:18 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 *************** *** 62,131 **** 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. --- 65,177 ---- 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 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 ! ! def _marshaled_dispatch(self, data): ! """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. ! """ ! ! params, method = xmlrpclib.loads(data) ! ! # generate response ! try: ! 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,175 **** --- 216,260 ---- return apply(func, params) else: raise Exception('method "%s" is not supported' % method) + + + class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler, + SimpleXMLRPCDispatcher): + """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"])) + response = self.server._marshaled_dispatch(data) + 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.""" *************** *** 178,248 **** 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)) server.register_function(pow) --- 263,312 ---- 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: ! 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)) server.register_function(pow)