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