blob: 5324e9213823d03f256ce32b1b66ccb9c164fa22 [file] [log] [blame]
Éric Araujo7c164122012-03-05 16:29:30 +01001r"""Simple XML-RPC Server.
Fredrik Lundhb329b712001-09-17 17:35:21 +00002
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):
24 # make all of the string functions available through
25 # string.func_name
26 import string
27 self.string = string
Martin v. Löwisd69663d2003-01-15 11:37:23 +000028 def _listMethods(self):
29 # implement this method so that system.listMethods
30 # knows to advertise the strings methods
31 return list_public_methods(self) + \
32 ['string.' + method for method in list_public_methods(self.string)]
Fredrik Lundhb329b712001-09-17 17:35:21 +000033 def pow(self, x, y): return pow(x, y)
34 def add(self, x, y) : return x + y
Tim Peters2c60f7a2003-01-29 03:49:43 +000035
Fredrik Lundhb329b712001-09-17 17:35:21 +000036server = SimpleXMLRPCServer(("localhost", 8000))
Martin v. Löwisd69663d2003-01-15 11:37:23 +000037server.register_introspection_functions()
Fredrik Lundhb329b712001-09-17 17:35:21 +000038server.register_instance(MyFuncs())
39server.serve_forever()
40
413. Install an instance with custom dispatch method:
42
43class Math:
Martin v. Löwisd69663d2003-01-15 11:37:23 +000044 def _listMethods(self):
45 # this method must be present for system.listMethods
46 # to work
47 return ['add', 'pow']
48 def _methodHelp(self, method):
49 # this method must be present for system.methodHelp
50 # to work
51 if method == 'add':
52 return "add(2,3) => 5"
53 elif method == 'pow':
54 return "pow(x, y[, z]) => number"
55 else:
56 # By convention, return empty
57 # string if no help is available
58 return ""
Fredrik Lundhb329b712001-09-17 17:35:21 +000059 def _dispatch(self, method, params):
60 if method == 'pow':
Martin v. Löwisd69663d2003-01-15 11:37:23 +000061 return pow(*params)
Fredrik Lundhb329b712001-09-17 17:35:21 +000062 elif method == 'add':
63 return params[0] + params[1]
64 else:
65 raise 'bad method'
Martin v. Löwisd69663d2003-01-15 11:37:23 +000066
Fredrik Lundhb329b712001-09-17 17:35:21 +000067server = SimpleXMLRPCServer(("localhost", 8000))
Martin v. Löwisd69663d2003-01-15 11:37:23 +000068server.register_introspection_functions()
Fredrik Lundhb329b712001-09-17 17:35:21 +000069server.register_instance(Math())
70server.serve_forever()
71
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000724. Subclass SimpleXMLRPCServer:
Fredrik Lundhb329b712001-09-17 17:35:21 +000073
Martin v. Löwisd69663d2003-01-15 11:37:23 +000074class MathServer(SimpleXMLRPCServer):
Fredrik Lundhb329b712001-09-17 17:35:21 +000075 def _dispatch(self, method, params):
76 try:
77 # We are forcing the 'export_' prefix on methods that are
78 # callable through XML-RPC to prevent potential security
79 # problems
80 func = getattr(self, 'export_' + method)
81 except AttributeError:
82 raise Exception('method "%s" is not supported' % method)
83 else:
Martin v. Löwisd69663d2003-01-15 11:37:23 +000084 return func(*params)
Fredrik Lundhb329b712001-09-17 17:35:21 +000085
86 def export_add(self, x, y):
87 return x + y
88
Martin v. Löwisd69663d2003-01-15 11:37:23 +000089server = MathServer(("localhost", 8000))
Fredrik Lundhb329b712001-09-17 17:35:21 +000090server.serve_forever()
Martin v. Löwisd69663d2003-01-15 11:37:23 +000091
925. CGI script:
93
94server = CGIXMLRPCRequestHandler()
95server.register_function(pow)
96server.handle_request()
Fredrik Lundhb329b712001-09-17 17:35:21 +000097"""
98
99# Written by Brian Quinlan (brian@sweetapp.com).
100# Based on code written by Fredrik Lundh.
101
102import xmlrpclib
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000103from xmlrpclib import Fault
Georg Brandle152a772008-05-24 18:31:28 +0000104import SocketServer
Fredrik Lundhb329b712001-09-17 17:35:21 +0000105import BaseHTTPServer
106import sys
Anthony Baxtere29002c2006-04-12 12:07:31 +0000107import os
Facundo Batista7f686fc2007-08-17 19:16:44 +0000108import traceback
Kristján Valur Jónssone0078602009-06-28 21:04:17 +0000109import re
Anthony Baxtere29002c2006-04-12 12:07:31 +0000110try:
111 import fcntl
112except ImportError:
113 fcntl = None
Fredrik Lundhb329b712001-09-17 17:35:21 +0000114
Guido van Rossumd0641422005-02-03 15:01:24 +0000115def resolve_dotted_attribute(obj, attr, allow_dotted_names=True):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000116 """resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d
Fredrik Lundhb329b712001-09-17 17:35:21 +0000117
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000118 Resolves a dotted attribute name to an object. Raises
119 an AttributeError if any attribute in the chain starts with a '_'.
Guido van Rossumd0641422005-02-03 15:01:24 +0000120
121 If the optional allow_dotted_names argument is false, dots are not
122 supported and this function operates similar to getattr(obj, attr).
Fredrik Lundhb329b712001-09-17 17:35:21 +0000123 """
124
Guido van Rossumd0641422005-02-03 15:01:24 +0000125 if allow_dotted_names:
126 attrs = attr.split('.')
127 else:
128 attrs = [attr]
129
130 for i in attrs:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000131 if i.startswith('_'):
132 raise AttributeError(
133 'attempt to access private attribute "%s"' % i
134 )
135 else:
136 obj = getattr(obj,i)
137 return obj
Fredrik Lundhb329b712001-09-17 17:35:21 +0000138
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000139def list_public_methods(obj):
140 """Returns a list of attribute strings, found in the specified
141 object, which represent callable attributes"""
142
143 return [member for member in dir(obj)
144 if not member.startswith('_') and
Brett Cannon0a0f6082008-08-03 22:57:23 +0000145 hasattr(getattr(obj, member), '__call__')]
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000146
147def remove_duplicates(lst):
148 """remove_duplicates([2,2,2,1,3,3]) => [3,1,2]
149
150 Returns a copy of a list without duplicates. Every list
151 item must be hashable and the order of the items in the
152 resulting list is not defined.
Tim Peters2c60f7a2003-01-29 03:49:43 +0000153 """
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000154 u = {}
155 for x in lst:
156 u[x] = 1
157
158 return u.keys()
159
160class SimpleXMLRPCDispatcher:
161 """Mix-in class that dispatches XML-RPC requests.
162
163 This class is used to register XML-RPC method handlers
Kristján Valur Jónsson429677e2009-08-27 23:13:18 +0000164 and then to dispatch them. This class doesn't need to be
165 instanced directly when used by SimpleXMLRPCServer but it
Georg Brandle1ff2142010-10-27 07:09:22 +0000166 can be instanced when used by the MultiPathXMLRPCServer.
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000167 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000168
Matthias Klosea5d58c82009-04-05 21:00:48 +0000169 def __init__(self, allow_none=False, encoding=None):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000170 self.funcs = {}
171 self.instance = None
Andrew M. Kuchling10a16de2005-12-04 16:34:40 +0000172 self.allow_none = allow_none
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000173 self.encoding = encoding
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000174
Guido van Rossumd0641422005-02-03 15:01:24 +0000175 def register_instance(self, instance, allow_dotted_names=False):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000176 """Registers an instance to respond to XML-RPC requests.
177
178 Only one instance can be installed at a time.
179
180 If the registered instance has a _dispatch method then that
181 method will be called with the name of the XML-RPC method and
Georg Brandl7eb4b7d2005-07-22 21:49:32 +0000182 its parameters as a tuple
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000183 e.g. instance._dispatch('add',(2,3))
184
185 If the registered instance does not have a _dispatch method
186 then the instance will be searched to find a matching method
187 and, if found, will be called. Methods beginning with an '_'
188 are considered private and will not be called by
189 SimpleXMLRPCServer.
190
Martin Panterbf2dca92016-07-11 07:51:37 +0000191 If a registered function matches an XML-RPC request, then it
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000192 will be called instead of the registered instance.
Guido van Rossumd0641422005-02-03 15:01:24 +0000193
194 If the optional allow_dotted_names argument is true and the
195 instance does not have a _dispatch method, method names
196 containing dots are supported and resolved, as long as none of
197 the name segments start with an '_'.
198
199 *** SECURITY WARNING: ***
200
201 Enabling the allow_dotted_names options allows intruders
202 to access your module's global variables and may allow
203 intruders to execute arbitrary code on your machine. Only
204 use this option on a secure, closed network.
205
Fredrik Lundhb329b712001-09-17 17:35:21 +0000206 """
207
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000208 self.instance = instance
Guido van Rossumd0641422005-02-03 15:01:24 +0000209 self.allow_dotted_names = allow_dotted_names
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000210
211 def register_function(self, function, name = None):
212 """Registers a function to respond to XML-RPC requests.
213
214 The optional name argument can be used to set a Unicode name
215 for the function.
216 """
217
218 if name is None:
219 name = function.__name__
220 self.funcs[name] = function
221
222 def register_introspection_functions(self):
223 """Registers the XML-RPC introspection methods in the system
224 namespace.
225
226 see http://xmlrpc.usefulinc.com/doc/reserved.html
227 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000228
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000229 self.funcs.update({'system.listMethods' : self.system_listMethods,
230 'system.methodSignature' : self.system_methodSignature,
231 'system.methodHelp' : self.system_methodHelp})
232
233 def register_multicall_functions(self):
234 """Registers the XML-RPC multicall method in the system
235 namespace.
236
237 see http://www.xmlrpc.com/discuss/msgReader$1208"""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000238
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000239 self.funcs.update({'system.multicall' : self.system_multicall})
Tim Peters2c60f7a2003-01-29 03:49:43 +0000240
Kristján Valur Jónsson429677e2009-08-27 23:13:18 +0000241 def _marshaled_dispatch(self, data, dispatch_method = None, path = None):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000242 """Dispatches an XML-RPC method from marshalled (XML) data.
Tim Peters2c60f7a2003-01-29 03:49:43 +0000243
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000244 XML-RPC methods are dispatched from the marshalled (XML) data
245 using the _dispatch method and the result is returned as
246 marshalled data. For backwards compatibility, a dispatch
Tim Peters2c60f7a2003-01-29 03:49:43 +0000247 function can be provided as an argument (see comment in
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000248 SimpleXMLRPCRequestHandler.do_POST) but overriding the
Ezio Melottic2077b02011-03-16 12:34:31 +0200249 existing method through subclassing is the preferred means
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000250 of changing method dispatch behavior.
251 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000252
Fredrik Lundhb329b712001-09-17 17:35:21 +0000253 try:
Georg Brandlb9120e72006-06-01 12:30:46 +0000254 params, method = xmlrpclib.loads(data)
255
256 # generate response
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000257 if dispatch_method is not None:
258 response = dispatch_method(method, params)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000259 else:
Fredrik Lundhb329b712001-09-17 17:35:21 +0000260 response = self._dispatch(method, params)
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000261 # wrap response in a singleton tuple
262 response = (response,)
Tim Peters536cf992005-12-25 23:18:31 +0000263 response = xmlrpclib.dumps(response, methodresponse=1,
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000264 allow_none=self.allow_none, encoding=self.encoding)
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000265 except Fault, fault:
Tim Peters536cf992005-12-25 23:18:31 +0000266 response = xmlrpclib.dumps(fault, allow_none=self.allow_none,
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000267 encoding=self.encoding)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000268 except:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000269 # report exception back to server
Andrew M. Kuchlinga5453c42006-09-05 13:15:41 +0000270 exc_type, exc_value, exc_tb = sys.exc_info()
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000271 response = xmlrpclib.dumps(
Andrew M. Kuchlinga5453c42006-09-05 13:15:41 +0000272 xmlrpclib.Fault(1, "%s:%s" % (exc_type, exc_value)),
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000273 encoding=self.encoding, allow_none=self.allow_none,
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000274 )
275
276 return response
277
278 def system_listMethods(self):
279 """system.listMethods() => ['add', 'subtract', 'multiple']
280
281 Returns a list of the methods supported by the server."""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000282
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000283 methods = self.funcs.keys()
284 if self.instance is not None:
285 # Instance can implement _listMethod to return a list of
286 # methods
287 if hasattr(self.instance, '_listMethods'):
288 methods = remove_duplicates(
289 methods + self.instance._listMethods()
290 )
291 # if the instance has a _dispatch method then we
292 # don't have enough information to provide a list
293 # of methods
294 elif not hasattr(self.instance, '_dispatch'):
295 methods = remove_duplicates(
296 methods + list_public_methods(self.instance)
297 )
298 methods.sort()
299 return methods
Tim Peters2c60f7a2003-01-29 03:49:43 +0000300
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000301 def system_methodSignature(self, method_name):
302 """system.methodSignature('add') => [double, int, int]
303
Brett Cannonb9b5f162004-10-03 23:21:44 +0000304 Returns a list describing the signature of the method. In the
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000305 above example, the add method takes two integers as arguments
306 and returns a double result.
307
308 This server does NOT support system.methodSignature."""
309
310 # See http://xmlrpc.usefulinc.com/doc/sysmethodsig.html
Tim Peters2c60f7a2003-01-29 03:49:43 +0000311
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000312 return 'signatures not supported'
313
314 def system_methodHelp(self, method_name):
315 """system.methodHelp('add') => "Adds two integers together"
316
317 Returns a string containing documentation for the specified method."""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000318
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000319 method = None
Brett Cannon0a0f6082008-08-03 22:57:23 +0000320 if method_name in self.funcs:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000321 method = self.funcs[method_name]
322 elif self.instance is not None:
323 # Instance can implement _methodHelp to return help for a method
324 if hasattr(self.instance, '_methodHelp'):
325 return self.instance._methodHelp(method_name)
326 # if the instance has a _dispatch method then we
327 # don't have enough information to provide help
328 elif not hasattr(self.instance, '_dispatch'):
329 try:
330 method = resolve_dotted_attribute(
331 self.instance,
Guido van Rossumd0641422005-02-03 15:01:24 +0000332 method_name,
333 self.allow_dotted_names
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000334 )
335 except AttributeError:
336 pass
337
338 # Note that we aren't checking that the method actually
339 # be a callable object of some kind
340 if method is None:
341 return ""
Fredrik Lundhb329b712001-09-17 17:35:21 +0000342 else:
Neal Norwitz732911f2003-06-29 04:16:28 +0000343 import pydoc
Neal Norwitz3f401f02003-06-29 04:19:37 +0000344 return pydoc.getdoc(method)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000345
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000346 def system_multicall(self, call_list):
347 """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \
348[[4], ...]
Fredrik Lundhb329b712001-09-17 17:35:21 +0000349
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000350 Allows the caller to package multiple XML-RPC calls into a single
351 request.
352
Tim Peters2c60f7a2003-01-29 03:49:43 +0000353 See http://www.xmlrpc.com/discuss/msgReader$1208
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000354 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000355
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000356 results = []
357 for call in call_list:
358 method_name = call['methodName']
359 params = call['params']
360
361 try:
362 # XXX A marshalling error in any response will fail the entire
363 # multicall. If someone cares they should fix this.
364 results.append([self._dispatch(method_name, params)])
365 except Fault, fault:
366 results.append(
367 {'faultCode' : fault.faultCode,
368 'faultString' : fault.faultString}
369 )
370 except:
Andrew M. Kuchlinga5453c42006-09-05 13:15:41 +0000371 exc_type, exc_value, exc_tb = sys.exc_info()
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000372 results.append(
373 {'faultCode' : 1,
Andrew M. Kuchlinga5453c42006-09-05 13:15:41 +0000374 'faultString' : "%s:%s" % (exc_type, exc_value)}
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000375 )
376 return results
Tim Peters2c60f7a2003-01-29 03:49:43 +0000377
Fredrik Lundhb329b712001-09-17 17:35:21 +0000378 def _dispatch(self, method, params):
379 """Dispatches the XML-RPC method.
380
381 XML-RPC calls are forwarded to a registered function that
382 matches the called XML-RPC method name. If no such function
383 exists then the call is forwarded to the registered instance,
384 if available.
385
386 If the registered instance has a _dispatch method then that
387 method will be called with the name of the XML-RPC method and
Georg Brandl7eb4b7d2005-07-22 21:49:32 +0000388 its parameters as a tuple
Fredrik Lundhb329b712001-09-17 17:35:21 +0000389 e.g. instance._dispatch('add',(2,3))
390
391 If the registered instance does not have a _dispatch method
392 then the instance will be searched to find a matching method
393 and, if found, will be called.
394
395 Methods beginning with an '_' are considered private and will
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000396 not be called.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000397 """
398
Fredrik Lundhb329b712001-09-17 17:35:21 +0000399 func = None
400 try:
401 # check to see if a matching function has been registered
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000402 func = self.funcs[method]
Fredrik Lundhb329b712001-09-17 17:35:21 +0000403 except KeyError:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000404 if self.instance is not None:
Fredrik Lundhb329b712001-09-17 17:35:21 +0000405 # check for a _dispatch method
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000406 if hasattr(self.instance, '_dispatch'):
407 return self.instance._dispatch(method, params)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000408 else:
409 # call instance method directly
410 try:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000411 func = resolve_dotted_attribute(
412 self.instance,
Guido van Rossumd0641422005-02-03 15:01:24 +0000413 method,
414 self.allow_dotted_names
Fredrik Lundhb329b712001-09-17 17:35:21 +0000415 )
416 except AttributeError:
417 pass
418
419 if func is not None:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000420 return func(*params)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000421 else:
422 raise Exception('method "%s" is not supported' % method)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000423
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000424class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
425 """Simple XML-RPC request handler class.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000426
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000427 Handles all HTTP POST requests and attempts to decode them as
428 XML-RPC requests.
429 """
430
Andrew M. Kuchling622f1442006-05-31 14:08:48 +0000431 # Class attribute listing the accessible path components;
432 # paths not on this list will result in a 404 error.
433 rpc_paths = ('/', '/RPC2')
434
Kristján Valur Jónssone0078602009-06-28 21:04:17 +0000435 #if not None, encode responses larger than this, if possible
436 encode_threshold = 1400 #a common MTU
437
438 #Override form StreamRequestHandler: full buffering of output
439 #and no Nagle.
440 wbufsize = -1
441 disable_nagle_algorithm = True
442
443 # a re to match a gzip Accept-Encoding
444 aepattern = re.compile(r"""
445 \s* ([^\s;]+) \s* #content-coding
446 (;\s* q \s*=\s* ([0-9\.]+))? #q
447 """, re.VERBOSE | re.IGNORECASE)
448
449 def accept_encodings(self):
450 r = {}
451 ae = self.headers.get("Accept-Encoding", "")
452 for e in ae.split(","):
453 match = self.aepattern.match(e)
454 if match:
455 v = match.group(3)
456 v = float(v) if v else 1.0
457 r[match.group(1)] = v
458 return r
459
Andrew M. Kuchling622f1442006-05-31 14:08:48 +0000460 def is_rpc_path_valid(self):
461 if self.rpc_paths:
462 return self.path in self.rpc_paths
463 else:
464 # If .rpc_paths is empty, just assume all paths are legal
465 return True
466
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000467 def do_POST(self):
468 """Handles the HTTP POST request.
469
470 Attempts to interpret all HTTP POST requests as XML-RPC calls,
471 which are forwarded to the server's _dispatch method for handling.
472 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000473
Andrew M. Kuchling622f1442006-05-31 14:08:48 +0000474 # Check that the path is legal
475 if not self.is_rpc_path_valid():
476 self.report_404()
477 return
Tim Peters5535da02006-06-01 13:41:46 +0000478
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000479 try:
Tim Peters536cf992005-12-25 23:18:31 +0000480 # Get arguments by reading body of request.
481 # We read this in chunks to avoid straining
Andrew M. Kuchlinge63fde72005-12-04 15:36:57 +0000482 # socket.read(); around the 10 or 15Mb mark, some platforms
483 # begin to have problems (bug #792570).
484 max_chunk_size = 10*1024*1024
485 size_remaining = int(self.headers["content-length"])
486 L = []
487 while size_remaining:
488 chunk_size = min(size_remaining, max_chunk_size)
Charles-François Natali66f3cc62012-02-18 14:15:38 +0100489 chunk = self.rfile.read(chunk_size)
490 if not chunk:
491 break
492 L.append(chunk)
Andrew M. Kuchlinge63fde72005-12-04 15:36:57 +0000493 size_remaining -= len(L[-1])
494 data = ''.join(L)
495
Kristján Valur Jónssone0078602009-06-28 21:04:17 +0000496 data = self.decode_request_content(data)
497 if data is None:
498 return #response has been sent
499
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000500 # In previous versions of SimpleXMLRPCServer, _dispatch
501 # could be overridden in this class, instead of in
502 # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
503 # check to see if a subclass implements _dispatch and dispatch
504 # using that method if present.
505 response = self.server._marshaled_dispatch(
Kristján Valur Jónsson429677e2009-08-27 23:13:18 +0000506 data, getattr(self, '_dispatch', None), self.path
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000507 )
Facundo Batista7f686fc2007-08-17 19:16:44 +0000508 except Exception, e: # This should only happen if the module is buggy
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000509 # internal error, report as HTTP server error
510 self.send_response(500)
Facundo Batista7f686fc2007-08-17 19:16:44 +0000511
512 # Send information about the exception if requested
513 if hasattr(self.server, '_send_traceback_header') and \
514 self.server._send_traceback_header:
515 self.send_header("X-exception", str(e))
516 self.send_header("X-traceback", traceback.format_exc())
517
Kristján Valur Jónssone0078602009-06-28 21:04:17 +0000518 self.send_header("Content-length", "0")
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000519 self.end_headers()
520 else:
521 # got a valid XML RPC response
522 self.send_response(200)
523 self.send_header("Content-type", "text/xml")
Kristján Valur Jónssone0078602009-06-28 21:04:17 +0000524 if self.encode_threshold is not None:
525 if len(response) > self.encode_threshold:
526 q = self.accept_encodings().get("gzip", 0)
527 if q:
Kristján Valur Jónsson6d755902009-07-19 22:14:00 +0000528 try:
529 response = xmlrpclib.gzip_encode(response)
530 self.send_header("Content-Encoding", "gzip")
531 except NotImplementedError:
532 pass
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000533 self.send_header("Content-length", str(len(response)))
534 self.end_headers()
535 self.wfile.write(response)
536
Kristján Valur Jónssone0078602009-06-28 21:04:17 +0000537 def decode_request_content(self, data):
538 #support gzip encoding of request
539 encoding = self.headers.get("content-encoding", "identity").lower()
540 if encoding == "identity":
541 return data
542 if encoding == "gzip":
543 try:
544 return xmlrpclib.gzip_decode(data)
Kristján Valur Jónsson6d755902009-07-19 22:14:00 +0000545 except NotImplementedError:
546 self.send_response(501, "encoding %r not supported" % encoding)
Kristján Valur Jónssone0078602009-06-28 21:04:17 +0000547 except ValueError:
548 self.send_response(400, "error decoding gzip content")
549 else:
550 self.send_response(501, "encoding %r not supported" % encoding)
551 self.send_header("Content-length", "0")
552 self.end_headers()
Tim Peters2c60f7a2003-01-29 03:49:43 +0000553
Andrew M. Kuchling622f1442006-05-31 14:08:48 +0000554 def report_404 (self):
555 # Report a 404 error
Tim Peters5535da02006-06-01 13:41:46 +0000556 self.send_response(404)
557 response = 'No such page'
558 self.send_header("Content-type", "text/plain")
559 self.send_header("Content-length", str(len(response)))
560 self.end_headers()
561 self.wfile.write(response)
Andrew M. Kuchling622f1442006-05-31 14:08:48 +0000562
Fredrik Lundhb329b712001-09-17 17:35:21 +0000563 def log_request(self, code='-', size='-'):
564 """Selectively log an accepted request."""
565
566 if self.server.logRequests:
567 BaseHTTPServer.BaseHTTPRequestHandler.log_request(self, code, size)
568
Georg Brandle152a772008-05-24 18:31:28 +0000569class SimpleXMLRPCServer(SocketServer.TCPServer,
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000570 SimpleXMLRPCDispatcher):
Fredrik Lundhb329b712001-09-17 17:35:21 +0000571 """Simple XML-RPC server.
572
573 Simple XML-RPC server that allows functions and a single instance
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000574 to be installed to handle requests. The default implementation
575 attempts to dispatch XML-RPC calls to the functions or instance
576 installed in the server. Override the _dispatch method inhereted
577 from SimpleXMLRPCDispatcher to change this behavior.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000578 """
579
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000580 allow_reuse_address = True
581
Facundo Batista7f686fc2007-08-17 19:16:44 +0000582 # Warning: this is for debugging purposes only! Never set this to True in
583 # production code, as will be sending out sensitive information (exception
584 # and stack trace details) when exceptions are raised inside
585 # SimpleXMLRPCRequestHandler.do_POST
586 _send_traceback_header = False
587
Fredrik Lundhb329b712001-09-17 17:35:21 +0000588 def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
Collin Winterae041062007-03-10 14:41:48 +0000589 logRequests=True, allow_none=False, encoding=None, bind_and_activate=True):
Fredrik Lundhb329b712001-09-17 17:35:21 +0000590 self.logRequests = logRequests
Tim Peters2c60f7a2003-01-29 03:49:43 +0000591
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000592 SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
Georg Brandle152a772008-05-24 18:31:28 +0000593 SocketServer.TCPServer.__init__(self, addr, requestHandler, bind_and_activate)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000594
Tim Peters536cf992005-12-25 23:18:31 +0000595 # [Bug #1222790] If possible, set close-on-exec flag; if a
596 # method spawns a subprocess, the subprocess shouldn't have
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000597 # the listening socket open.
Anthony Baxtere29002c2006-04-12 12:07:31 +0000598 if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000599 flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
600 flags |= fcntl.FD_CLOEXEC
601 fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
602
Kristján Valur Jónsson429677e2009-08-27 23:13:18 +0000603class MultiPathXMLRPCServer(SimpleXMLRPCServer):
604 """Multipath XML-RPC Server
605 This specialization of SimpleXMLRPCServer allows the user to create
606 multiple Dispatcher instances and assign them to different
607 HTTP request paths. This makes it possible to run two or more
608 'virtual XML-RPC servers' at the same port.
609 Make sure that the requestHandler accepts the paths in question.
610 """
611 def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
612 logRequests=True, allow_none=False, encoding=None, bind_and_activate=True):
613
614 SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, allow_none,
615 encoding, bind_and_activate)
616 self.dispatchers = {}
617 self.allow_none = allow_none
618 self.encoding = encoding
619
620 def add_dispatcher(self, path, dispatcher):
621 self.dispatchers[path] = dispatcher
622 return dispatcher
623
624 def get_dispatcher(self, path):
625 return self.dispatchers[path]
626
627 def _marshaled_dispatch(self, data, dispatch_method = None, path = None):
628 try:
629 response = self.dispatchers[path]._marshaled_dispatch(
630 data, dispatch_method, path)
631 except:
632 # report low level exception back to server
633 # (each dispatcher should have handled their own
634 # exceptions)
635 exc_type, exc_value = sys.exc_info()[:2]
636 response = xmlrpclib.dumps(
637 xmlrpclib.Fault(1, "%s:%s" % (exc_type, exc_value)),
638 encoding=self.encoding, allow_none=self.allow_none)
639 return response
640
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000641class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher):
642 """Simple handler for XML-RPC data passed through CGI."""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000643
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000644 def __init__(self, allow_none=False, encoding=None):
645 SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000646
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000647 def handle_xmlrpc(self, request_text):
648 """Handle a single XML-RPC request"""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000649
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000650 response = self._marshaled_dispatch(request_text)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000651
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000652 print 'Content-Type: text/xml'
653 print 'Content-Length: %d' % len(response)
654 print
Martin v. Löwis9c5ea502003-05-01 05:05:09 +0000655 sys.stdout.write(response)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000656
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000657 def handle_get(self):
658 """Handle a single HTTP GET request.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000659
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000660 Default implementation indicates an error because
661 XML-RPC uses the POST method.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000662 """
663
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000664 code = 400
665 message, explain = \
666 BaseHTTPServer.BaseHTTPRequestHandler.responses[code]
Tim Peters2c60f7a2003-01-29 03:49:43 +0000667
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000668 response = BaseHTTPServer.DEFAULT_ERROR_MESSAGE % \
669 {
Tim Peters2c60f7a2003-01-29 03:49:43 +0000670 'code' : code,
671 'message' : message,
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000672 'explain' : explain
673 }
674 print 'Status: %d %s' % (code, message)
Senthil Kumaran20d114c2009-04-01 20:26:33 +0000675 print 'Content-Type: %s' % BaseHTTPServer.DEFAULT_ERROR_CONTENT_TYPE
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000676 print 'Content-Length: %d' % len(response)
677 print
Neal Norwitz732911f2003-06-29 04:16:28 +0000678 sys.stdout.write(response)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000679
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000680 def handle_request(self, request_text = None):
681 """Handle a single XML-RPC request passed through a CGI post method.
Tim Peters2c60f7a2003-01-29 03:49:43 +0000682
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000683 If no XML data is given then it is read from stdin. The resulting
684 XML-RPC response is printed to stdout along with the correct HTTP
685 headers.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000686 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000687
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000688 if request_text is None and \
689 os.environ.get('REQUEST_METHOD', None) == 'GET':
690 self.handle_get()
691 else:
692 # POST data is normally available through stdin
Georg Brandle92d4b62009-04-01 04:21:14 +0000693 try:
694 length = int(os.environ.get('CONTENT_LENGTH', None))
Georg Brandl61fce382009-04-01 15:23:43 +0000695 except (TypeError, ValueError):
Georg Brandle92d4b62009-04-01 04:21:14 +0000696 length = -1
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000697 if request_text is None:
Georg Brandle92d4b62009-04-01 04:21:14 +0000698 request_text = sys.stdin.read(length)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000699
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000700 self.handle_xmlrpc(request_text)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000701
Fredrik Lundhb329b712001-09-17 17:35:21 +0000702if __name__ == '__main__':
Andrew M. Kuchlingb0a1e6b2006-04-21 12:57:35 +0000703 print 'Running XML-RPC server on port 8000'
Fredrik Lundhb329b712001-09-17 17:35:21 +0000704 server = SimpleXMLRPCServer(("localhost", 8000))
705 server.register_function(pow)
706 server.register_function(lambda x,y: x+y, 'add')
Senthil Kumaran01109732014-01-12 16:04:08 -0800707 server.register_multicall_functions()
Fredrik Lundhb329b712001-09-17 17:35:21 +0000708 server.serve_forever()