mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 19:34:08 +00:00 
			
		
		
		
	every time this gets called; move it out as a global helper function.
    Simplify the call to the _dispatch() method of the registered instance.
		
	
			
		
			
				
	
	
		
			250 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			250 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""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 SimpleXMLRPCRequestHandler
 | 
						|
class.
 | 
						|
 | 
						|
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 pow(self, x, y): return pow(x, y)
 | 
						|
    def add(self, x, y) : return x + y
 | 
						|
server = SimpleXMLRPCServer(("localhost", 8000))
 | 
						|
server.register_instance(MyFuncs())
 | 
						|
server.serve_forever()
 | 
						|
 | 
						|
3. Install an instance with custom dispatch method:
 | 
						|
 | 
						|
class Math:
 | 
						|
    def _dispatch(self, method, params):
 | 
						|
        if method == 'pow':
 | 
						|
            return apply(pow, params)
 | 
						|
        elif method == 'add':
 | 
						|
            return params[0] + params[1]
 | 
						|
        else:
 | 
						|
            raise 'bad method'
 | 
						|
server = SimpleXMLRPCServer(("localhost", 8000))
 | 
						|
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
 | 
						|
            # 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 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.
 | 
						|
 | 
						|
        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
 | 
						|
        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.
 | 
						|
        """
 | 
						|
 | 
						|
        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:
 | 
						|
                        pass
 | 
						|
 | 
						|
        if func is not None:
 | 
						|
            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))
 | 
						|
    server.register_function(pow)
 | 
						|
    server.register_function(lambda x,y: x+y, 'add')
 | 
						|
    server.serve_forever()
 |