1 """Simple XMLRPC Server.
  2 
  3 This module can be used to create simple XMLRPC servers
  4 by creating a server and either installing functions, a
  5 class instance, or by extending the SimpleXMLRPCRequestHandler
  6 class.
  7 
  8 A list of possible usage patterns follows:
  9 
 10 1. Install functions:
 11 
 12 server = SimpleXMLRPCServer(("localhost", 8000))
 13 server.register_function(pow)
 14 server.register_function(lambda x,y: x+y, 'add')
 15 server.serve_forever()
 16 
 17 2. Install an instance:
 18 
 19 class MyFuncs:
 20     def __init__(self):
 21         # make all of the string functions available through
 22         # string.func_name
 23         import string
 24         self.string = string
 25     def pow(self, x, y): return pow(x, y)
 26     def add(self, x, y) : return x + y
 27 server = SimpleXMLRPCServer(("localhost", 8000))
 28 server.register_instance(MyFuncs())
 29 server.serve_forever()
 30 
 31 3. Install an instance with custom dispatch method:
 32 
 33 class Math:
 34     def _dispatch(self, method, params):
 35         if method == 'pow':
 36             return apply(pow, params)
 37         elif method == 'add':
 38             return params[0] + params[1]
 39         else:
 40             raise 'bad method'
 41 server = SimpleXMLRPCServer(("localhost", 8000))
 42 server.register_instance(Math())
 43 server.serve_forever()
 44 
 45 4. Subclass SimpleXMLRPCRequestHandler:
 46 
 47 class MathHandler(SimpleXMLRPCRequestHandler):
 48     def _dispatch(self, method, params):
 49         try:
 50             # We are forcing the 'export_' prefix on methods that are
 51             # callable through XMLRPC to prevent potential security
 52             # problems
 53             func = getattr(self, 'export_' + method)
 54         except AttributeError:
 55             raise Exception('method "%s" is not supported' % method)
 56         else:
 57             return apply(func, params)
 58 
 59     def log_message(self, format, *args):
 60         pass # maybe do something fancy like write the messages to a file
 61 
 62     def export_add(self, x, y):
 63         return x + y
 64 
 65 server = SimpleXMLRPCServer(("localhost", 8000), MathHandler)
 66 server.serve_forever()
 67 """
 68 
 69 # Written by Brian Quinlan (brian@sweetapp.com).
 70 # Based on code written by Fredrik Lundh.
 71 
 72 import xmlrpclib
 73 import SocketServer
 74 import BaseHTTPServer
 75 import sys
 76 
 77 class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
 78     """Simple XMLRPC request handler class.
 79 
 80     Handles all HTTP POST requests and attempts to decode them as
 81     XMLRPC requests.
 82 
 83     XMLRPC requests are dispatched to the _dispatch method, which
 84     may be overriden by subclasses. The default implementation attempts
 85     to dispatch XMLRPC calls to the functions or instance installed
 86     in the server.
 87     """
 88 
 89     def do_POST(self):
 90         """Handles the HTTP POST request.
 91 
 92         Attempts to interpret all HTTP POST requests as XMLRPC calls,
 93         which are forwarded to the _dispatch method for handling.
 94         """
 95 
 96         try:
 97             # get arguments
 98             data = self.rfile.read(int(self.headers["content-length"]))
 99             params, method = xmlrpclib.loads(data)
100 
101             # generate response
102             try:
103                 response = self._dispatch(method, params)
104                 # wrap response in a singleton tuple
105                 response = (response,)
106             except:
107                 # report exception back to server
108                 response = xmlrpclib.dumps(
109                     xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value))
110                     )
111             else:
112                 response = xmlrpclib.dumps(response, methodresponse=1)
113         except:
114             # internal error, report as HTTP server error
115             self.send_response(500)
116             self.end_headers()
117         else:
118             # got a valid XML RPC response
119             self.send_response(200)
120             self.send_header("Content-type", "text/xml")
121             self.send_header("Content-length", str(len(response)))
122             self.end_headers()
123             self.wfile.write(response)
124 
125             # shut down the connection
126             self.wfile.flush()
127             self.connection.shutdown(1)
128 
129     def _dispatch(self, method, params):
130         """Dispatches the XMLRPC method.
131 
132         XMLRPC calls are forwarded to a registered function that
133         matches the called XMLRPC method name. If no such function
134         exists then the call is forwarded to the registered instance,
135         if available.
136 
137         If the registered instance has a _dispatch method then that
138         method will be called with the name of the XMLRPC method and
139         it's parameters as a tuple
140         e.g. instance._dispatch('add',(2,3))
141 
142         If the registered instance does not have a _dispatch method
143         then the instance will be searched to find a matching method
144         and, if found, will be called.
145 
146         Methods beginning with an '_' are considered private and will
147         not be called by SimpleXMLRPCServer.
148         """
149 
150         def resolve_dotted_attribute(obj, attr):
151             """resolve_dotted_attribute(math, 'cos.__doc__') => math.cos.__doc__
152 
153             Resolves a dotted attribute name to an object. Raises
154             an AttributeError if any attribute in the chain starts
155             with a '_'.
156             """
157             for i in attr.split('.'):
158                 if i.startswith('_'):
159                     raise AttributeError(
160                         'attempt to access private attribute "%s"' % i
161                         )
162                 else:
163                     obj = getattr(obj,i)
164             return obj
165 
166         func = None
167         try:
168             # check to see if a matching function has been registered
169             func = self.server.funcs[method]
170         except KeyError:
171             if self.server.instance is not None:
172                 # check for a _dispatch method
173                 if hasattr(self.server.instance, '_dispatch'):
174                     return apply(
175                         getattr(self.server.instance,'_dispatch'),
176                         (method, params)
177                         )
178                 else:
179                     # call instance method directly
180                     try:
181                         func = resolve_dotted_attribute(
182                             self.server.instance,
183                             method
184                             )
185                     except AttributeError:
186                         pass
187 
188         if func is not None:
189             return apply(func, params)
190         else:
191             raise Exception('method "%s" is not supported' % method)
192 
193     def log_request(self, code='-', size='-'):
194         """Selectively log an accepted request."""
195 
196         if self.server.logRequests:
197             BaseHTTPServer.BaseHTTPRequestHandler.log_request(self, code, size)
198 
199 class SimpleXMLRPCServer(SocketServer.TCPServer):
200     """Simple XMLRPC server.
201 
202     Simple XMLRPC server that allows functions and a single instance
203     to be installed to handle requests.
204     """
205 
206     def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
207                  logRequests=1):
208         self.funcs = {}
209         self.logRequests = logRequests
210         self.instance = None
211         SocketServer.TCPServer.__init__(self, addr, requestHandler)
212 
213     def register_instance(self, instance):
214         """Registers an instance to respond to XMLRPC requests.
215 
216         Only one instance can be installed at a time.
217 
218         If the registered instance has a _dispatch method then that
219         method will be called with the name of the XMLRPC method and
220         it's parameters as a tuple
221         e.g. instance._dispatch('add',(2,3))
222 
223         If the registered instance does not have a _dispatch method
224         then the instance will be searched to find a matching method
225         and, if found, will be called.
226 
227         Methods beginning with an '_' are considered private and will
228         not be called by SimpleXMLRPCServer.
229 
230         If a registered function matches a XMLRPC request, then it
231         will be called instead of the registered instance.
232         """
233 
234         self.instance = instance
235 
236     def register_function(self, function, name = None):
237         """Registers a function to respond to XMLRPC requests.
238 
239         The optional name argument can be used to set a Unicode name
240         for the function.
241 
242         If an instance is also registered then it will only be called
243         if a matching function is not found.
244         """
245 
246         if name is None:
247             name = function.__name__
248         self.funcs[name] = function
249 
250 if __name__ == '__main__':
251     server = SimpleXMLRPCServer(("localhost", 8000))
252     server.register_function(pow)
253     server.register_function(lambda x,y: x+y, 'add')
254     server.serve_forever()


syntax highlighted by Code2HTML, v. 0.9.1