blob: 5f6e9d0bdecf58d0cad7105b89c2cff3be4567e4 [file] [log] [blame]
Fredrik Lundhb329b712001-09-17 17:35:21 +00001"""Simple XML-RPC Server.
2
3This module can be used to create simple XML-RPC servers
4by creating a server and either installing functions, a
Martin v. Löwisd69663d2003-01-15 11:37:23 +00005class instance, or by extending the SimpleXMLRPCServer
Fredrik Lundhb329b712001-09-17 17:35:21 +00006class.
7
Martin v. Löwisd69663d2003-01-15 11:37:23 +00008It can also be used to handle XML-RPC requests in a CGI
9environment using CGIXMLRPCRequestHandler.
10
Fredrik Lundhb329b712001-09-17 17:35:21 +000011A list of possible usage patterns follows:
12
131. Install functions:
14
15server = SimpleXMLRPCServer(("localhost", 8000))
16server.register_function(pow)
17server.register_function(lambda x,y: x+y, 'add')
18server.serve_forever()
19
202. Install an instance:
21
22class MyFuncs:
23 def __init__(self):
Neal Norwitz9d72bb42007-04-17 08:48:32 +000024 # make all of the sys functions available through sys.func_name
25 import sys
26 self.sys = sys
Martin v. Löwisd69663d2003-01-15 11:37:23 +000027 def _listMethods(self):
28 # implement this method so that system.listMethods
Neal Norwitz9d72bb42007-04-17 08:48:32 +000029 # knows to advertise the sys methods
Martin v. Löwisd69663d2003-01-15 11:37:23 +000030 return list_public_methods(self) + \
Neal Norwitz9d72bb42007-04-17 08:48:32 +000031 ['sys.' + method for method in list_public_methods(self.sys)]
Fredrik Lundhb329b712001-09-17 17:35:21 +000032 def pow(self, x, y): return pow(x, y)
33 def add(self, x, y) : return x + y
Tim Peters2c60f7a2003-01-29 03:49:43 +000034
Fredrik Lundhb329b712001-09-17 17:35:21 +000035server = SimpleXMLRPCServer(("localhost", 8000))
Martin v. Löwisd69663d2003-01-15 11:37:23 +000036server.register_introspection_functions()
Fredrik Lundhb329b712001-09-17 17:35:21 +000037server.register_instance(MyFuncs())
38server.serve_forever()
39
403. Install an instance with custom dispatch method:
41
42class Math:
Martin v. Löwisd69663d2003-01-15 11:37:23 +000043 def _listMethods(self):
44 # this method must be present for system.listMethods
45 # to work
46 return ['add', 'pow']
47 def _methodHelp(self, method):
48 # this method must be present for system.methodHelp
49 # to work
50 if method == 'add':
51 return "add(2,3) => 5"
52 elif method == 'pow':
53 return "pow(x, y[, z]) => number"
54 else:
55 # By convention, return empty
56 # string if no help is available
57 return ""
Fredrik Lundhb329b712001-09-17 17:35:21 +000058 def _dispatch(self, method, params):
59 if method == 'pow':
Martin v. Löwisd69663d2003-01-15 11:37:23 +000060 return pow(*params)
Fredrik Lundhb329b712001-09-17 17:35:21 +000061 elif method == 'add':
62 return params[0] + params[1]
63 else:
64 raise 'bad method'
Martin v. Löwisd69663d2003-01-15 11:37:23 +000065
Fredrik Lundhb329b712001-09-17 17:35:21 +000066server = SimpleXMLRPCServer(("localhost", 8000))
Martin v. Löwisd69663d2003-01-15 11:37:23 +000067server.register_introspection_functions()
Fredrik Lundhb329b712001-09-17 17:35:21 +000068server.register_instance(Math())
69server.serve_forever()
70
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000714. Subclass SimpleXMLRPCServer:
Fredrik Lundhb329b712001-09-17 17:35:21 +000072
Martin v. Löwisd69663d2003-01-15 11:37:23 +000073class MathServer(SimpleXMLRPCServer):
Fredrik Lundhb329b712001-09-17 17:35:21 +000074 def _dispatch(self, method, params):
75 try:
76 # We are forcing the 'export_' prefix on methods that are
77 # callable through XML-RPC to prevent potential security
78 # problems
79 func = getattr(self, 'export_' + method)
80 except AttributeError:
81 raise Exception('method "%s" is not supported' % method)
82 else:
Martin v. Löwisd69663d2003-01-15 11:37:23 +000083 return func(*params)
Fredrik Lundhb329b712001-09-17 17:35:21 +000084
85 def export_add(self, x, y):
86 return x + y
87
Martin v. Löwisd69663d2003-01-15 11:37:23 +000088server = MathServer(("localhost", 8000))
Fredrik Lundhb329b712001-09-17 17:35:21 +000089server.serve_forever()
Martin v. Löwisd69663d2003-01-15 11:37:23 +000090
915. CGI script:
92
93server = CGIXMLRPCRequestHandler()
94server.register_function(pow)
95server.handle_request()
Fredrik Lundhb329b712001-09-17 17:35:21 +000096"""
97
98# Written by Brian Quinlan (brian@sweetapp.com).
99# Based on code written by Fredrik Lundh.
100
101import xmlrpclib
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000102from xmlrpclib import Fault
Fredrik Lundhb329b712001-09-17 17:35:21 +0000103import SocketServer
104import BaseHTTPServer
105import sys
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000106import os
Guido van Rossum61e21b52007-08-20 19:06:03 +0000107import traceback
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000108try:
109 import fcntl
110except ImportError:
111 fcntl = None
Fredrik Lundhb329b712001-09-17 17:35:21 +0000112
Guido van Rossumd0641422005-02-03 15:01:24 +0000113def resolve_dotted_attribute(obj, attr, allow_dotted_names=True):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000114 """resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d
Fredrik Lundhb329b712001-09-17 17:35:21 +0000115
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000116 Resolves a dotted attribute name to an object. Raises
117 an AttributeError if any attribute in the chain starts with a '_'.
Guido van Rossumd0641422005-02-03 15:01:24 +0000118
119 If the optional allow_dotted_names argument is false, dots are not
120 supported and this function operates similar to getattr(obj, attr).
Fredrik Lundhb329b712001-09-17 17:35:21 +0000121 """
122
Guido van Rossumd0641422005-02-03 15:01:24 +0000123 if allow_dotted_names:
124 attrs = attr.split('.')
125 else:
126 attrs = [attr]
127
128 for i in attrs:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000129 if i.startswith('_'):
130 raise AttributeError(
131 'attempt to access private attribute "%s"' % i
132 )
133 else:
134 obj = getattr(obj,i)
135 return obj
Fredrik Lundhb329b712001-09-17 17:35:21 +0000136
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000137def list_public_methods(obj):
138 """Returns a list of attribute strings, found in the specified
139 object, which represent callable attributes"""
140
141 return [member for member in dir(obj)
142 if not member.startswith('_') and
Guido van Rossumd59da4b2007-05-22 18:11:13 +0000143 hasattr(getattr(obj, member), '__call__')]
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000144
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000145class SimpleXMLRPCDispatcher:
146 """Mix-in class that dispatches XML-RPC requests.
147
148 This class is used to register XML-RPC method handlers
149 and then to dispatch them. There should never be any
150 reason to instantiate this class directly.
151 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000152
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000153 def __init__(self, allow_none, encoding):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000154 self.funcs = {}
155 self.instance = None
Andrew M. Kuchling10a16de2005-12-04 16:34:40 +0000156 self.allow_none = allow_none
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000157 self.encoding = encoding
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000158
Guido van Rossumd0641422005-02-03 15:01:24 +0000159 def register_instance(self, instance, allow_dotted_names=False):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000160 """Registers an instance to respond to XML-RPC requests.
161
162 Only one instance can be installed at a time.
163
164 If the registered instance has a _dispatch method then that
165 method will be called with the name of the XML-RPC method and
Georg Brandl7eb4b7d2005-07-22 21:49:32 +0000166 its parameters as a tuple
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000167 e.g. instance._dispatch('add',(2,3))
168
169 If the registered instance does not have a _dispatch method
170 then the instance will be searched to find a matching method
171 and, if found, will be called. Methods beginning with an '_'
172 are considered private and will not be called by
173 SimpleXMLRPCServer.
174
175 If a registered function matches a XML-RPC request, then it
176 will be called instead of the registered instance.
Guido van Rossumd0641422005-02-03 15:01:24 +0000177
178 If the optional allow_dotted_names argument is true and the
179 instance does not have a _dispatch method, method names
180 containing dots are supported and resolved, as long as none of
181 the name segments start with an '_'.
182
183 *** SECURITY WARNING: ***
184
185 Enabling the allow_dotted_names options allows intruders
186 to access your module's global variables and may allow
187 intruders to execute arbitrary code on your machine. Only
188 use this option on a secure, closed network.
189
Fredrik Lundhb329b712001-09-17 17:35:21 +0000190 """
191
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000192 self.instance = instance
Guido van Rossumd0641422005-02-03 15:01:24 +0000193 self.allow_dotted_names = allow_dotted_names
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000194
195 def register_function(self, function, name = None):
196 """Registers a function to respond to XML-RPC requests.
197
198 The optional name argument can be used to set a Unicode name
199 for the function.
200 """
201
202 if name is None:
203 name = function.__name__
204 self.funcs[name] = function
205
206 def register_introspection_functions(self):
207 """Registers the XML-RPC introspection methods in the system
208 namespace.
209
210 see http://xmlrpc.usefulinc.com/doc/reserved.html
211 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000212
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000213 self.funcs.update({'system.listMethods' : self.system_listMethods,
214 'system.methodSignature' : self.system_methodSignature,
215 'system.methodHelp' : self.system_methodHelp})
216
217 def register_multicall_functions(self):
218 """Registers the XML-RPC multicall method in the system
219 namespace.
220
221 see http://www.xmlrpc.com/discuss/msgReader$1208"""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000222
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000223 self.funcs.update({'system.multicall' : self.system_multicall})
Tim Peters2c60f7a2003-01-29 03:49:43 +0000224
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000225 def _marshaled_dispatch(self, data, dispatch_method = None):
226 """Dispatches an XML-RPC method from marshalled (XML) data.
Tim Peters2c60f7a2003-01-29 03:49:43 +0000227
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000228 XML-RPC methods are dispatched from the marshalled (XML) data
229 using the _dispatch method and the result is returned as
230 marshalled data. For backwards compatibility, a dispatch
Tim Peters2c60f7a2003-01-29 03:49:43 +0000231 function can be provided as an argument (see comment in
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000232 SimpleXMLRPCRequestHandler.do_POST) but overriding the
233 existing method through subclassing is the prefered means
234 of changing method dispatch behavior.
235 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000236
Fredrik Lundhb329b712001-09-17 17:35:21 +0000237 try:
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000238 params, method = xmlrpclib.loads(data)
239
240 # generate response
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000241 if dispatch_method is not None:
242 response = dispatch_method(method, params)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000243 else:
Fredrik Lundhb329b712001-09-17 17:35:21 +0000244 response = self._dispatch(method, params)
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000245 # wrap response in a singleton tuple
246 response = (response,)
Tim Peters536cf992005-12-25 23:18:31 +0000247 response = xmlrpclib.dumps(response, methodresponse=1,
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000248 allow_none=self.allow_none, encoding=self.encoding)
Guido van Rossumb940e112007-01-10 16:19:56 +0000249 except Fault as fault:
Tim Peters536cf992005-12-25 23:18:31 +0000250 response = xmlrpclib.dumps(fault, allow_none=self.allow_none,
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000251 encoding=self.encoding)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000252 except:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000253 # report exception back to server
Thomas Wouters89f507f2006-12-13 04:49:30 +0000254 exc_type, exc_value, exc_tb = sys.exc_info()
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000255 response = xmlrpclib.dumps(
Thomas Wouters89f507f2006-12-13 04:49:30 +0000256 xmlrpclib.Fault(1, "%s:%s" % (exc_type, exc_value)),
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000257 encoding=self.encoding, allow_none=self.allow_none,
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000258 )
259
260 return response
261
262 def system_listMethods(self):
263 """system.listMethods() => ['add', 'subtract', 'multiple']
264
265 Returns a list of the methods supported by the server."""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000266
Hye-Shik Chang96042862007-08-19 10:49:11 +0000267 methods = set(self.funcs.keys())
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000268 if self.instance is not None:
269 # Instance can implement _listMethod to return a list of
270 # methods
271 if hasattr(self.instance, '_listMethods'):
Hye-Shik Chang96042862007-08-19 10:49:11 +0000272 methods |= set(self.instance._listMethods())
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000273 # if the instance has a _dispatch method then we
274 # don't have enough information to provide a list
275 # of methods
276 elif not hasattr(self.instance, '_dispatch'):
Hye-Shik Chang96042862007-08-19 10:49:11 +0000277 methods |= set(list_public_methods(self.instance))
278 return sorted(methods)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000279
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000280 def system_methodSignature(self, method_name):
281 """system.methodSignature('add') => [double, int, int]
282
Brett Cannonb9b5f162004-10-03 23:21:44 +0000283 Returns a list describing the signature of the method. In the
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000284 above example, the add method takes two integers as arguments
285 and returns a double result.
286
287 This server does NOT support system.methodSignature."""
288
289 # See http://xmlrpc.usefulinc.com/doc/sysmethodsig.html
Tim Peters2c60f7a2003-01-29 03:49:43 +0000290
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000291 return 'signatures not supported'
292
293 def system_methodHelp(self, method_name):
294 """system.methodHelp('add') => "Adds two integers together"
295
296 Returns a string containing documentation for the specified method."""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000297
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000298 method = None
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000299 if method_name in self.funcs:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000300 method = self.funcs[method_name]
301 elif self.instance is not None:
302 # Instance can implement _methodHelp to return help for a method
303 if hasattr(self.instance, '_methodHelp'):
304 return self.instance._methodHelp(method_name)
305 # if the instance has a _dispatch method then we
306 # don't have enough information to provide help
307 elif not hasattr(self.instance, '_dispatch'):
308 try:
309 method = resolve_dotted_attribute(
310 self.instance,
Guido van Rossumd0641422005-02-03 15:01:24 +0000311 method_name,
312 self.allow_dotted_names
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000313 )
314 except AttributeError:
315 pass
316
317 # Note that we aren't checking that the method actually
318 # be a callable object of some kind
319 if method is None:
320 return ""
Fredrik Lundhb329b712001-09-17 17:35:21 +0000321 else:
Neal Norwitz732911f2003-06-29 04:16:28 +0000322 import pydoc
Neal Norwitz3f401f02003-06-29 04:19:37 +0000323 return pydoc.getdoc(method)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000324
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000325 def system_multicall(self, call_list):
326 """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \
327[[4], ...]
Fredrik Lundhb329b712001-09-17 17:35:21 +0000328
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000329 Allows the caller to package multiple XML-RPC calls into a single
330 request.
331
Tim Peters2c60f7a2003-01-29 03:49:43 +0000332 See http://www.xmlrpc.com/discuss/msgReader$1208
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000333 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000334
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000335 results = []
336 for call in call_list:
337 method_name = call['methodName']
338 params = call['params']
339
340 try:
341 # XXX A marshalling error in any response will fail the entire
342 # multicall. If someone cares they should fix this.
343 results.append([self._dispatch(method_name, params)])
Guido van Rossumb940e112007-01-10 16:19:56 +0000344 except Fault as fault:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000345 results.append(
346 {'faultCode' : fault.faultCode,
347 'faultString' : fault.faultString}
348 )
349 except:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000350 exc_type, exc_value, exc_tb = sys.exc_info()
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000351 results.append(
352 {'faultCode' : 1,
Thomas Wouters89f507f2006-12-13 04:49:30 +0000353 'faultString' : "%s:%s" % (exc_type, exc_value)}
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000354 )
355 return results
Tim Peters2c60f7a2003-01-29 03:49:43 +0000356
Fredrik Lundhb329b712001-09-17 17:35:21 +0000357 def _dispatch(self, method, params):
358 """Dispatches the XML-RPC method.
359
360 XML-RPC calls are forwarded to a registered function that
361 matches the called XML-RPC method name. If no such function
362 exists then the call is forwarded to the registered instance,
363 if available.
364
365 If the registered instance has a _dispatch method then that
366 method will be called with the name of the XML-RPC method and
Georg Brandl7eb4b7d2005-07-22 21:49:32 +0000367 its parameters as a tuple
Fredrik Lundhb329b712001-09-17 17:35:21 +0000368 e.g. instance._dispatch('add',(2,3))
369
370 If the registered instance does not have a _dispatch method
371 then the instance will be searched to find a matching method
372 and, if found, will be called.
373
374 Methods beginning with an '_' are considered private and will
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000375 not be called.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000376 """
377
Fredrik Lundhb329b712001-09-17 17:35:21 +0000378 func = None
379 try:
380 # check to see if a matching function has been registered
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000381 func = self.funcs[method]
Fredrik Lundhb329b712001-09-17 17:35:21 +0000382 except KeyError:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000383 if self.instance is not None:
Fredrik Lundhb329b712001-09-17 17:35:21 +0000384 # check for a _dispatch method
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000385 if hasattr(self.instance, '_dispatch'):
386 return self.instance._dispatch(method, params)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000387 else:
388 # call instance method directly
389 try:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000390 func = resolve_dotted_attribute(
391 self.instance,
Guido van Rossumd0641422005-02-03 15:01:24 +0000392 method,
393 self.allow_dotted_names
Fredrik Lundhb329b712001-09-17 17:35:21 +0000394 )
395 except AttributeError:
396 pass
397
398 if func is not None:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000399 return func(*params)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000400 else:
401 raise Exception('method "%s" is not supported' % method)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000402
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000403class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
404 """Simple XML-RPC request handler class.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000405
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000406 Handles all HTTP POST requests and attempts to decode them as
407 XML-RPC requests.
408 """
409
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000410 # Class attribute listing the accessible path components;
411 # paths not on this list will result in a 404 error.
412 rpc_paths = ('/', '/RPC2')
413
414 def is_rpc_path_valid(self):
415 if self.rpc_paths:
416 return self.path in self.rpc_paths
417 else:
418 # If .rpc_paths is empty, just assume all paths are legal
419 return True
420
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000421 def do_POST(self):
422 """Handles the HTTP POST request.
423
424 Attempts to interpret all HTTP POST requests as XML-RPC calls,
425 which are forwarded to the server's _dispatch method for handling.
426 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000427
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000428 # Check that the path is legal
429 if not self.is_rpc_path_valid():
430 self.report_404()
431 return
432
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000433 try:
Tim Peters536cf992005-12-25 23:18:31 +0000434 # Get arguments by reading body of request.
435 # We read this in chunks to avoid straining
Andrew M. Kuchlinge63fde72005-12-04 15:36:57 +0000436 # socket.read(); around the 10 or 15Mb mark, some platforms
437 # begin to have problems (bug #792570).
438 max_chunk_size = 10*1024*1024
439 size_remaining = int(self.headers["content-length"])
440 L = []
441 while size_remaining:
442 chunk_size = min(size_remaining, max_chunk_size)
443 L.append(self.rfile.read(chunk_size))
444 size_remaining -= len(L[-1])
Hye-Shik Chang96042862007-08-19 10:49:11 +0000445 data = b''.join(L)
Andrew M. Kuchlinge63fde72005-12-04 15:36:57 +0000446
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000447 # In previous versions of SimpleXMLRPCServer, _dispatch
448 # could be overridden in this class, instead of in
449 # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
450 # check to see if a subclass implements _dispatch and dispatch
451 # using that method if present.
452 response = self.server._marshaled_dispatch(
453 data, getattr(self, '_dispatch', None)
454 )
Guido van Rossum61e21b52007-08-20 19:06:03 +0000455 except Exception as e: # This should only happen if the module is buggy
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000456 # internal error, report as HTTP server error
457 self.send_response(500)
Guido van Rossum61e21b52007-08-20 19:06:03 +0000458
459 # Send information about the exception if requested
460 if hasattr(self.server, '_send_traceback_header') and \
461 self.server._send_traceback_header:
462 self.send_header("X-exception", str(e))
463 self.send_header("X-traceback", traceback.format_exc())
464
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000465 self.end_headers()
466 else:
467 # got a valid XML RPC response
468 self.send_response(200)
469 self.send_header("Content-type", "text/xml")
470 self.send_header("Content-length", str(len(response)))
471 self.end_headers()
472 self.wfile.write(response)
473
474 # shut down the connection
475 self.wfile.flush()
476 self.connection.shutdown(1)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000477
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000478 def report_404 (self):
479 # Report a 404 error
480 self.send_response(404)
481 response = 'No such page'
482 self.send_header("Content-type", "text/plain")
483 self.send_header("Content-length", str(len(response)))
484 self.end_headers()
485 self.wfile.write(response)
486 # shut down the connection
487 self.wfile.flush()
488 self.connection.shutdown(1)
489
Fredrik Lundhb329b712001-09-17 17:35:21 +0000490 def log_request(self, code='-', size='-'):
491 """Selectively log an accepted request."""
492
493 if self.server.logRequests:
494 BaseHTTPServer.BaseHTTPRequestHandler.log_request(self, code, size)
495
Tim Peters2c60f7a2003-01-29 03:49:43 +0000496class SimpleXMLRPCServer(SocketServer.TCPServer,
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000497 SimpleXMLRPCDispatcher):
Fredrik Lundhb329b712001-09-17 17:35:21 +0000498 """Simple XML-RPC server.
499
500 Simple XML-RPC server that allows functions and a single instance
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000501 to be installed to handle requests. The default implementation
502 attempts to dispatch XML-RPC calls to the functions or instance
503 installed in the server. Override the _dispatch method inhereted
504 from SimpleXMLRPCDispatcher to change this behavior.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000505 """
506
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000507 allow_reuse_address = True
508
Guido van Rossum61e21b52007-08-20 19:06:03 +0000509 # Warning: this is for debugging purposes only! Never set this to True in
510 # production code, as will be sending out sensitive information (exception
511 # and stack trace details) when exceptions are raised inside
512 # SimpleXMLRPCRequestHandler.do_POST
513 _send_traceback_header = False
514
Fredrik Lundhb329b712001-09-17 17:35:21 +0000515 def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
Guido van Rossumd8faa362007-04-27 19:54:29 +0000516 logRequests=True, allow_none=False, encoding=None, bind_and_activate=True):
Fredrik Lundhb329b712001-09-17 17:35:21 +0000517 self.logRequests = logRequests
Tim Peters2c60f7a2003-01-29 03:49:43 +0000518
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000519 SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000520 SocketServer.TCPServer.__init__(self, addr, requestHandler, bind_and_activate)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000521
Tim Peters536cf992005-12-25 23:18:31 +0000522 # [Bug #1222790] If possible, set close-on-exec flag; if a
523 # method spawns a subprocess, the subprocess shouldn't have
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000524 # the listening socket open.
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000525 if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000526 flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
527 flags |= fcntl.FD_CLOEXEC
528 fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
529
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000530class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher):
531 """Simple handler for XML-RPC data passed through CGI."""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000532
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000533 def __init__(self, allow_none=False, encoding=None):
534 SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000535
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000536 def handle_xmlrpc(self, request_text):
537 """Handle a single XML-RPC request"""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000538
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000539 response = self._marshaled_dispatch(request_text)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000540
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000541 print('Content-Type: text/xml')
542 print('Content-Length: %d' % len(response))
543 print()
Martin v. Löwis9c5ea502003-05-01 05:05:09 +0000544 sys.stdout.write(response)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000545
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000546 def handle_get(self):
547 """Handle a single HTTP GET request.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000548
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000549 Default implementation indicates an error because
550 XML-RPC uses the POST method.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000551 """
552
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000553 code = 400
554 message, explain = \
555 BaseHTTPServer.BaseHTTPRequestHandler.responses[code]
Tim Peters2c60f7a2003-01-29 03:49:43 +0000556
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000557 response = BaseHTTPServer.DEFAULT_ERROR_MESSAGE % \
558 {
Tim Peters2c60f7a2003-01-29 03:49:43 +0000559 'code' : code,
560 'message' : message,
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000561 'explain' : explain
562 }
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000563 print('Status: %d %s' % (code, message))
564 print('Content-Type: text/html')
565 print('Content-Length: %d' % len(response))
566 print()
Neal Norwitz732911f2003-06-29 04:16:28 +0000567 sys.stdout.write(response)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000568
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000569 def handle_request(self, request_text = None):
570 """Handle a single XML-RPC request passed through a CGI post method.
Tim Peters2c60f7a2003-01-29 03:49:43 +0000571
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000572 If no XML data is given then it is read from stdin. The resulting
573 XML-RPC response is printed to stdout along with the correct HTTP
574 headers.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000575 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000576
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000577 if request_text is None and \
578 os.environ.get('REQUEST_METHOD', None) == 'GET':
579 self.handle_get()
580 else:
581 # POST data is normally available through stdin
582 if request_text is None:
Tim Peters2c60f7a2003-01-29 03:49:43 +0000583 request_text = sys.stdin.read()
Fredrik Lundhb329b712001-09-17 17:35:21 +0000584
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000585 self.handle_xmlrpc(request_text)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000586
Fredrik Lundhb329b712001-09-17 17:35:21 +0000587if __name__ == '__main__':
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000588 print('Running XML-RPC server on port 8000')
Fredrik Lundhb329b712001-09-17 17:35:21 +0000589 server = SimpleXMLRPCServer(("localhost", 8000))
590 server.register_function(pow)
591 server.register_function(lambda x,y: x+y, 'add')
592 server.serve_forever()