blob: fd17026583717238d3d766ea86e2655a30d3d30c [file] [log] [blame]
Éric Araujoc432a9d2012-03-05 15:45:08 +01001r"""XML-RPC Servers.
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
Georg Brandl38eceaa2008-05-26 11:14:17 +000011The Doc* classes can be used to create XML-RPC servers that
12serve pydoc-style documentation in response to HTTP
13GET requests. This documentation is dynamically generated
14based on the functions and methods registered with the
15server.
16
Fredrik Lundhb329b712001-09-17 17:35:21 +000017A list of possible usage patterns follows:
18
191. Install functions:
20
21server = SimpleXMLRPCServer(("localhost", 8000))
22server.register_function(pow)
23server.register_function(lambda x,y: x+y, 'add')
24server.serve_forever()
25
262. Install an instance:
27
28class MyFuncs:
29 def __init__(self):
Neal Norwitz9d72bb42007-04-17 08:48:32 +000030 # make all of the sys functions available through sys.func_name
31 import sys
32 self.sys = sys
Martin v. Löwisd69663d2003-01-15 11:37:23 +000033 def _listMethods(self):
34 # implement this method so that system.listMethods
Neal Norwitz9d72bb42007-04-17 08:48:32 +000035 # knows to advertise the sys methods
Martin v. Löwisd69663d2003-01-15 11:37:23 +000036 return list_public_methods(self) + \
Neal Norwitz9d72bb42007-04-17 08:48:32 +000037 ['sys.' + method for method in list_public_methods(self.sys)]
Fredrik Lundhb329b712001-09-17 17:35:21 +000038 def pow(self, x, y): return pow(x, y)
39 def add(self, x, y) : return x + y
Tim Peters2c60f7a2003-01-29 03:49:43 +000040
Fredrik Lundhb329b712001-09-17 17:35:21 +000041server = SimpleXMLRPCServer(("localhost", 8000))
Martin v. Löwisd69663d2003-01-15 11:37:23 +000042server.register_introspection_functions()
Fredrik Lundhb329b712001-09-17 17:35:21 +000043server.register_instance(MyFuncs())
44server.serve_forever()
45
463. Install an instance with custom dispatch method:
47
48class Math:
Martin v. Löwisd69663d2003-01-15 11:37:23 +000049 def _listMethods(self):
50 # this method must be present for system.listMethods
51 # to work
52 return ['add', 'pow']
53 def _methodHelp(self, method):
54 # this method must be present for system.methodHelp
55 # to work
56 if method == 'add':
57 return "add(2,3) => 5"
58 elif method == 'pow':
59 return "pow(x, y[, z]) => number"
60 else:
61 # By convention, return empty
62 # string if no help is available
63 return ""
Fredrik Lundhb329b712001-09-17 17:35:21 +000064 def _dispatch(self, method, params):
65 if method == 'pow':
Martin v. Löwisd69663d2003-01-15 11:37:23 +000066 return pow(*params)
Fredrik Lundhb329b712001-09-17 17:35:21 +000067 elif method == 'add':
68 return params[0] + params[1]
69 else:
Benjamin Peterson2d8c9172010-10-31 18:13:04 +000070 raise ValueError('bad method')
Martin v. Löwisd69663d2003-01-15 11:37:23 +000071
Fredrik Lundhb329b712001-09-17 17:35:21 +000072server = SimpleXMLRPCServer(("localhost", 8000))
Martin v. Löwisd69663d2003-01-15 11:37:23 +000073server.register_introspection_functions()
Fredrik Lundhb329b712001-09-17 17:35:21 +000074server.register_instance(Math())
75server.serve_forever()
76
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000774. Subclass SimpleXMLRPCServer:
Fredrik Lundhb329b712001-09-17 17:35:21 +000078
Martin v. Löwisd69663d2003-01-15 11:37:23 +000079class MathServer(SimpleXMLRPCServer):
Fredrik Lundhb329b712001-09-17 17:35:21 +000080 def _dispatch(self, method, params):
81 try:
82 # We are forcing the 'export_' prefix on methods that are
83 # callable through XML-RPC to prevent potential security
84 # problems
85 func = getattr(self, 'export_' + method)
86 except AttributeError:
87 raise Exception('method "%s" is not supported' % method)
88 else:
Martin v. Löwisd69663d2003-01-15 11:37:23 +000089 return func(*params)
Fredrik Lundhb329b712001-09-17 17:35:21 +000090
91 def export_add(self, x, y):
92 return x + y
93
Martin v. Löwisd69663d2003-01-15 11:37:23 +000094server = MathServer(("localhost", 8000))
Fredrik Lundhb329b712001-09-17 17:35:21 +000095server.serve_forever()
Martin v. Löwisd69663d2003-01-15 11:37:23 +000096
975. CGI script:
98
99server = CGIXMLRPCRequestHandler()
100server.register_function(pow)
101server.handle_request()
Fredrik Lundhb329b712001-09-17 17:35:21 +0000102"""
103
104# Written by Brian Quinlan (brian@sweetapp.com).
105# Based on code written by Fredrik Lundh.
106
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000107from xmlrpc.client import Fault, dumps, loads, gzip_encode, gzip_decode
Georg Brandl24420152008-05-26 16:32:26 +0000108from http.server import BaseHTTPRequestHandler
109import http.server
Alexandre Vassalottice261952008-05-12 02:31:37 +0000110import socketserver
Fredrik Lundhb329b712001-09-17 17:35:21 +0000111import sys
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000112import os
Georg Brandl38eceaa2008-05-26 11:14:17 +0000113import re
114import pydoc
115import inspect
Guido van Rossum61e21b52007-08-20 19:06:03 +0000116import traceback
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000117try:
118 import fcntl
119except ImportError:
120 fcntl = None
Fredrik Lundhb329b712001-09-17 17:35:21 +0000121
Guido van Rossumd0641422005-02-03 15:01:24 +0000122def resolve_dotted_attribute(obj, attr, allow_dotted_names=True):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000123 """resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d
Fredrik Lundhb329b712001-09-17 17:35:21 +0000124
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000125 Resolves a dotted attribute name to an object. Raises
126 an AttributeError if any attribute in the chain starts with a '_'.
Guido van Rossumd0641422005-02-03 15:01:24 +0000127
128 If the optional allow_dotted_names argument is false, dots are not
129 supported and this function operates similar to getattr(obj, attr).
Fredrik Lundhb329b712001-09-17 17:35:21 +0000130 """
131
Guido van Rossumd0641422005-02-03 15:01:24 +0000132 if allow_dotted_names:
133 attrs = attr.split('.')
134 else:
135 attrs = [attr]
136
137 for i in attrs:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000138 if i.startswith('_'):
139 raise AttributeError(
140 'attempt to access private attribute "%s"' % i
141 )
142 else:
143 obj = getattr(obj,i)
144 return obj
Fredrik Lundhb329b712001-09-17 17:35:21 +0000145
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000146def list_public_methods(obj):
147 """Returns a list of attribute strings, found in the specified
148 object, which represent callable attributes"""
149
150 return [member for member in dir(obj)
151 if not member.startswith('_') and
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200152 callable(getattr(obj, member))]
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000153
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000154class SimpleXMLRPCDispatcher:
155 """Mix-in class that dispatches XML-RPC requests.
156
157 This class is used to register XML-RPC method handlers
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000158 and then to dispatch them. This class doesn't need to be
159 instanced directly when used by SimpleXMLRPCServer but it
160 can be instanced when used by the MultiPathXMLRPCServer
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000161 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000162
Matthias Klosea3d29e82009-04-07 13:13:10 +0000163 def __init__(self, allow_none=False, encoding=None):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000164 self.funcs = {}
165 self.instance = None
Andrew M. Kuchling10a16de2005-12-04 16:34:40 +0000166 self.allow_none = allow_none
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000167 self.encoding = encoding or 'utf-8'
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000168
Guido van Rossumd0641422005-02-03 15:01:24 +0000169 def register_instance(self, instance, allow_dotted_names=False):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000170 """Registers an instance to respond to XML-RPC requests.
171
172 Only one instance can be installed at a time.
173
174 If the registered instance has a _dispatch method then that
175 method will be called with the name of the XML-RPC method and
Georg Brandl7eb4b7d2005-07-22 21:49:32 +0000176 its parameters as a tuple
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000177 e.g. instance._dispatch('add',(2,3))
178
179 If the registered instance does not have a _dispatch method
180 then the instance will be searched to find a matching method
181 and, if found, will be called. Methods beginning with an '_'
182 are considered private and will not be called by
183 SimpleXMLRPCServer.
184
185 If a registered function matches a XML-RPC request, then it
186 will be called instead of the registered instance.
Guido van Rossumd0641422005-02-03 15:01:24 +0000187
188 If the optional allow_dotted_names argument is true and the
189 instance does not have a _dispatch method, method names
190 containing dots are supported and resolved, as long as none of
191 the name segments start with an '_'.
192
193 *** SECURITY WARNING: ***
194
195 Enabling the allow_dotted_names options allows intruders
196 to access your module's global variables and may allow
197 intruders to execute arbitrary code on your machine. Only
198 use this option on a secure, closed network.
199
Fredrik Lundhb329b712001-09-17 17:35:21 +0000200 """
201
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000202 self.instance = instance
Guido van Rossumd0641422005-02-03 15:01:24 +0000203 self.allow_dotted_names = allow_dotted_names
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000204
Georg Brandlfe991052009-09-16 15:54:04 +0000205 def register_function(self, function, name=None):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000206 """Registers a function to respond to XML-RPC requests.
207
208 The optional name argument can be used to set a Unicode name
209 for the function.
210 """
211
212 if name is None:
213 name = function.__name__
214 self.funcs[name] = function
215
216 def register_introspection_functions(self):
217 """Registers the XML-RPC introspection methods in the system
218 namespace.
219
220 see http://xmlrpc.usefulinc.com/doc/reserved.html
221 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000222
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000223 self.funcs.update({'system.listMethods' : self.system_listMethods,
224 'system.methodSignature' : self.system_methodSignature,
225 'system.methodHelp' : self.system_methodHelp})
226
227 def register_multicall_functions(self):
228 """Registers the XML-RPC multicall method in the system
229 namespace.
230
231 see http://www.xmlrpc.com/discuss/msgReader$1208"""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000232
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000233 self.funcs.update({'system.multicall' : self.system_multicall})
Tim Peters2c60f7a2003-01-29 03:49:43 +0000234
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000235 def _marshaled_dispatch(self, data, dispatch_method = None, path = None):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000236 """Dispatches an XML-RPC method from marshalled (XML) data.
Tim Peters2c60f7a2003-01-29 03:49:43 +0000237
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000238 XML-RPC methods are dispatched from the marshalled (XML) data
239 using the _dispatch method and the result is returned as
240 marshalled data. For backwards compatibility, a dispatch
Tim Peters2c60f7a2003-01-29 03:49:43 +0000241 function can be provided as an argument (see comment in
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000242 SimpleXMLRPCRequestHandler.do_POST) but overriding the
Ezio Melotti13925002011-03-16 11:05:33 +0200243 existing method through subclassing is the preferred means
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000244 of changing method dispatch behavior.
245 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000246
Fredrik Lundhb329b712001-09-17 17:35:21 +0000247 try:
Georg Brandl38eceaa2008-05-26 11:14:17 +0000248 params, method = loads(data)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000249
250 # generate response
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000251 if dispatch_method is not None:
252 response = dispatch_method(method, params)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000253 else:
Fredrik Lundhb329b712001-09-17 17:35:21 +0000254 response = self._dispatch(method, params)
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000255 # wrap response in a singleton tuple
256 response = (response,)
Georg Brandl38eceaa2008-05-26 11:14:17 +0000257 response = dumps(response, methodresponse=1,
258 allow_none=self.allow_none, encoding=self.encoding)
Guido van Rossumb940e112007-01-10 16:19:56 +0000259 except Fault as fault:
Georg Brandl38eceaa2008-05-26 11:14:17 +0000260 response = dumps(fault, allow_none=self.allow_none,
261 encoding=self.encoding)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000262 except:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000263 # report exception back to server
Thomas Wouters89f507f2006-12-13 04:49:30 +0000264 exc_type, exc_value, exc_tb = sys.exc_info()
Georg Brandl38eceaa2008-05-26 11:14:17 +0000265 response = dumps(
266 Fault(1, "%s:%s" % (exc_type, exc_value)),
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000267 encoding=self.encoding, allow_none=self.allow_none,
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000268 )
269
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000270 return response.encode(self.encoding)
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000271
272 def system_listMethods(self):
273 """system.listMethods() => ['add', 'subtract', 'multiple']
274
275 Returns a list of the methods supported by the server."""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000276
Hye-Shik Chang96042862007-08-19 10:49:11 +0000277 methods = set(self.funcs.keys())
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000278 if self.instance is not None:
279 # Instance can implement _listMethod to return a list of
280 # methods
281 if hasattr(self.instance, '_listMethods'):
Hye-Shik Chang96042862007-08-19 10:49:11 +0000282 methods |= set(self.instance._listMethods())
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000283 # if the instance has a _dispatch method then we
284 # don't have enough information to provide a list
285 # of methods
286 elif not hasattr(self.instance, '_dispatch'):
Hye-Shik Chang96042862007-08-19 10:49:11 +0000287 methods |= set(list_public_methods(self.instance))
288 return sorted(methods)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000289
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000290 def system_methodSignature(self, method_name):
291 """system.methodSignature('add') => [double, int, int]
292
Brett Cannonb9b5f162004-10-03 23:21:44 +0000293 Returns a list describing the signature of the method. In the
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000294 above example, the add method takes two integers as arguments
295 and returns a double result.
296
297 This server does NOT support system.methodSignature."""
298
299 # See http://xmlrpc.usefulinc.com/doc/sysmethodsig.html
Tim Peters2c60f7a2003-01-29 03:49:43 +0000300
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000301 return 'signatures not supported'
302
303 def system_methodHelp(self, method_name):
304 """system.methodHelp('add') => "Adds two integers together"
305
306 Returns a string containing documentation for the specified method."""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000307
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000308 method = None
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000309 if method_name in self.funcs:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000310 method = self.funcs[method_name]
311 elif self.instance is not None:
312 # Instance can implement _methodHelp to return help for a method
313 if hasattr(self.instance, '_methodHelp'):
314 return self.instance._methodHelp(method_name)
315 # if the instance has a _dispatch method then we
316 # don't have enough information to provide help
317 elif not hasattr(self.instance, '_dispatch'):
318 try:
319 method = resolve_dotted_attribute(
320 self.instance,
Guido van Rossumd0641422005-02-03 15:01:24 +0000321 method_name,
322 self.allow_dotted_names
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000323 )
324 except AttributeError:
325 pass
326
327 # Note that we aren't checking that the method actually
328 # be a callable object of some kind
329 if method is None:
330 return ""
Fredrik Lundhb329b712001-09-17 17:35:21 +0000331 else:
Neal Norwitz3f401f02003-06-29 04:19:37 +0000332 return pydoc.getdoc(method)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000333
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000334 def system_multicall(self, call_list):
335 """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \
336[[4], ...]
Fredrik Lundhb329b712001-09-17 17:35:21 +0000337
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000338 Allows the caller to package multiple XML-RPC calls into a single
339 request.
340
Tim Peters2c60f7a2003-01-29 03:49:43 +0000341 See http://www.xmlrpc.com/discuss/msgReader$1208
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000342 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000343
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000344 results = []
345 for call in call_list:
346 method_name = call['methodName']
347 params = call['params']
348
349 try:
350 # XXX A marshalling error in any response will fail the entire
351 # multicall. If someone cares they should fix this.
352 results.append([self._dispatch(method_name, params)])
Guido van Rossumb940e112007-01-10 16:19:56 +0000353 except Fault as fault:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000354 results.append(
355 {'faultCode' : fault.faultCode,
356 'faultString' : fault.faultString}
357 )
358 except:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000359 exc_type, exc_value, exc_tb = sys.exc_info()
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000360 results.append(
361 {'faultCode' : 1,
Thomas Wouters89f507f2006-12-13 04:49:30 +0000362 'faultString' : "%s:%s" % (exc_type, exc_value)}
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000363 )
364 return results
Tim Peters2c60f7a2003-01-29 03:49:43 +0000365
Fredrik Lundhb329b712001-09-17 17:35:21 +0000366 def _dispatch(self, method, params):
367 """Dispatches the XML-RPC method.
368
369 XML-RPC calls are forwarded to a registered function that
370 matches the called XML-RPC method name. If no such function
371 exists then the call is forwarded to the registered instance,
372 if available.
373
374 If the registered instance has a _dispatch method then that
375 method will be called with the name of the XML-RPC method and
Georg Brandl7eb4b7d2005-07-22 21:49:32 +0000376 its parameters as a tuple
Fredrik Lundhb329b712001-09-17 17:35:21 +0000377 e.g. instance._dispatch('add',(2,3))
378
379 If the registered instance does not have a _dispatch method
380 then the instance will be searched to find a matching method
381 and, if found, will be called.
382
383 Methods beginning with an '_' are considered private and will
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000384 not be called.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000385 """
386
Fredrik Lundhb329b712001-09-17 17:35:21 +0000387 func = None
388 try:
389 # check to see if a matching function has been registered
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000390 func = self.funcs[method]
Fredrik Lundhb329b712001-09-17 17:35:21 +0000391 except KeyError:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000392 if self.instance is not None:
Fredrik Lundhb329b712001-09-17 17:35:21 +0000393 # check for a _dispatch method
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000394 if hasattr(self.instance, '_dispatch'):
395 return self.instance._dispatch(method, params)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000396 else:
397 # call instance method directly
398 try:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000399 func = resolve_dotted_attribute(
400 self.instance,
Guido van Rossumd0641422005-02-03 15:01:24 +0000401 method,
402 self.allow_dotted_names
Fredrik Lundhb329b712001-09-17 17:35:21 +0000403 )
404 except AttributeError:
405 pass
406
407 if func is not None:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000408 return func(*params)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000409 else:
410 raise Exception('method "%s" is not supported' % method)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000411
Georg Brandl24420152008-05-26 16:32:26 +0000412class SimpleXMLRPCRequestHandler(BaseHTTPRequestHandler):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000413 """Simple XML-RPC request handler class.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000414
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000415 Handles all HTTP POST requests and attempts to decode them as
416 XML-RPC requests.
417 """
418
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000419 # Class attribute listing the accessible path components;
420 # paths not on this list will result in a 404 error.
421 rpc_paths = ('/', '/RPC2')
422
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000423 #if not None, encode responses larger than this, if possible
424 encode_threshold = 1400 #a common MTU
425
426 #Override form StreamRequestHandler: full buffering of output
427 #and no Nagle.
428 wbufsize = -1
429 disable_nagle_algorithm = True
430
431 # a re to match a gzip Accept-Encoding
432 aepattern = re.compile(r"""
433 \s* ([^\s;]+) \s* #content-coding
434 (;\s* q \s*=\s* ([0-9\.]+))? #q
435 """, re.VERBOSE | re.IGNORECASE)
436
437 def accept_encodings(self):
438 r = {}
439 ae = self.headers.get("Accept-Encoding", "")
440 for e in ae.split(","):
441 match = self.aepattern.match(e)
442 if match:
443 v = match.group(3)
444 v = float(v) if v else 1.0
445 r[match.group(1)] = v
446 return r
447
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000448 def is_rpc_path_valid(self):
449 if self.rpc_paths:
450 return self.path in self.rpc_paths
451 else:
452 # If .rpc_paths is empty, just assume all paths are legal
453 return True
454
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000455 def do_POST(self):
456 """Handles the HTTP POST request.
457
458 Attempts to interpret all HTTP POST requests as XML-RPC calls,
459 which are forwarded to the server's _dispatch method for handling.
460 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000461
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000462 # Check that the path is legal
463 if not self.is_rpc_path_valid():
464 self.report_404()
465 return
466
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000467 try:
Tim Peters536cf992005-12-25 23:18:31 +0000468 # Get arguments by reading body of request.
469 # We read this in chunks to avoid straining
Andrew M. Kuchlinge63fde72005-12-04 15:36:57 +0000470 # socket.read(); around the 10 or 15Mb mark, some platforms
471 # begin to have problems (bug #792570).
472 max_chunk_size = 10*1024*1024
473 size_remaining = int(self.headers["content-length"])
474 L = []
475 while size_remaining:
476 chunk_size = min(size_remaining, max_chunk_size)
Charles-François Nataliec1712a2012-02-18 14:42:57 +0100477 chunk = self.rfile.read(chunk_size)
478 if not chunk:
479 break
480 L.append(chunk)
Andrew M. Kuchlinge63fde72005-12-04 15:36:57 +0000481 size_remaining -= len(L[-1])
Hye-Shik Chang96042862007-08-19 10:49:11 +0000482 data = b''.join(L)
Andrew M. Kuchlinge63fde72005-12-04 15:36:57 +0000483
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000484 data = self.decode_request_content(data)
485 if data is None:
486 return #response has been sent
487
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000488 # In previous versions of SimpleXMLRPCServer, _dispatch
489 # could be overridden in this class, instead of in
490 # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
491 # check to see if a subclass implements _dispatch and dispatch
492 # using that method if present.
493 response = self.server._marshaled_dispatch(
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000494 data, getattr(self, '_dispatch', None), self.path
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000495 )
Guido van Rossum61e21b52007-08-20 19:06:03 +0000496 except Exception as e: # This should only happen if the module is buggy
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000497 # internal error, report as HTTP server error
498 self.send_response(500)
Guido van Rossum61e21b52007-08-20 19:06:03 +0000499
500 # Send information about the exception if requested
501 if hasattr(self.server, '_send_traceback_header') and \
502 self.server._send_traceback_header:
503 self.send_header("X-exception", str(e))
Victor Stinner5bfe1462010-04-16 13:28:05 +0000504 trace = traceback.format_exc()
505 trace = str(trace.encode('ASCII', 'backslashreplace'), 'ASCII')
506 self.send_header("X-traceback", trace)
Guido van Rossum61e21b52007-08-20 19:06:03 +0000507
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000508 self.send_header("Content-length", "0")
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000509 self.end_headers()
510 else:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000511 self.send_response(200)
512 self.send_header("Content-type", "text/xml")
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000513 if self.encode_threshold is not None:
514 if len(response) > self.encode_threshold:
515 q = self.accept_encodings().get("gzip", 0)
516 if q:
Kristján Valur Jónssonaefde242009-07-19 22:29:24 +0000517 try:
518 response = gzip_encode(response)
519 self.send_header("Content-Encoding", "gzip")
520 except NotImplementedError:
521 pass
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000522 self.send_header("Content-length", str(len(response)))
523 self.end_headers()
524 self.wfile.write(response)
525
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000526 def decode_request_content(self, data):
527 #support gzip encoding of request
528 encoding = self.headers.get("content-encoding", "identity").lower()
529 if encoding == "identity":
530 return data
531 if encoding == "gzip":
532 try:
533 return gzip_decode(data)
Kristján Valur Jónssonaefde242009-07-19 22:29:24 +0000534 except NotImplementedError:
535 self.send_response(501, "encoding %r not supported" % encoding)
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000536 except ValueError:
537 self.send_response(400, "error decoding gzip content")
538 else:
539 self.send_response(501, "encoding %r not supported" % encoding)
540 self.send_header("Content-length", "0")
541 self.end_headers()
Tim Peters2c60f7a2003-01-29 03:49:43 +0000542
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000543 def report_404 (self):
544 # Report a 404 error
545 self.send_response(404)
Christian Heimes0aa93cd2007-12-08 18:38:20 +0000546 response = b'No such page'
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000547 self.send_header("Content-type", "text/plain")
548 self.send_header("Content-length", str(len(response)))
549 self.end_headers()
550 self.wfile.write(response)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000551
Fredrik Lundhb329b712001-09-17 17:35:21 +0000552 def log_request(self, code='-', size='-'):
553 """Selectively log an accepted request."""
554
555 if self.server.logRequests:
Georg Brandl24420152008-05-26 16:32:26 +0000556 BaseHTTPRequestHandler.log_request(self, code, size)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000557
Alexandre Vassalottice261952008-05-12 02:31:37 +0000558class SimpleXMLRPCServer(socketserver.TCPServer,
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000559 SimpleXMLRPCDispatcher):
Fredrik Lundhb329b712001-09-17 17:35:21 +0000560 """Simple XML-RPC server.
561
562 Simple XML-RPC server that allows functions and a single instance
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000563 to be installed to handle requests. The default implementation
564 attempts to dispatch XML-RPC calls to the functions or instance
Florent Xiclunac4fec932011-10-30 20:19:32 +0100565 installed in the server. Override the _dispatch method inherited
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000566 from SimpleXMLRPCDispatcher to change this behavior.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000567 """
568
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000569 allow_reuse_address = True
570
Guido van Rossum61e21b52007-08-20 19:06:03 +0000571 # Warning: this is for debugging purposes only! Never set this to True in
572 # production code, as will be sending out sensitive information (exception
573 # and stack trace details) when exceptions are raised inside
574 # SimpleXMLRPCRequestHandler.do_POST
575 _send_traceback_header = False
576
Fredrik Lundhb329b712001-09-17 17:35:21 +0000577 def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
Guido van Rossumd8faa362007-04-27 19:54:29 +0000578 logRequests=True, allow_none=False, encoding=None, bind_and_activate=True):
Fredrik Lundhb329b712001-09-17 17:35:21 +0000579 self.logRequests = logRequests
Tim Peters2c60f7a2003-01-29 03:49:43 +0000580
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000581 SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
Alexandre Vassalottice261952008-05-12 02:31:37 +0000582 socketserver.TCPServer.__init__(self, addr, requestHandler, bind_and_activate)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000583
Tim Peters536cf992005-12-25 23:18:31 +0000584 # [Bug #1222790] If possible, set close-on-exec flag; if a
585 # method spawns a subprocess, the subprocess shouldn't have
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000586 # the listening socket open.
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000587 if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000588 flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
589 flags |= fcntl.FD_CLOEXEC
590 fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
591
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000592class MultiPathXMLRPCServer(SimpleXMLRPCServer):
593 """Multipath XML-RPC Server
594 This specialization of SimpleXMLRPCServer allows the user to create
595 multiple Dispatcher instances and assign them to different
596 HTTP request paths. This makes it possible to run two or more
597 'virtual XML-RPC servers' at the same port.
598 Make sure that the requestHandler accepts the paths in question.
599 """
600 def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
601 logRequests=True, allow_none=False, encoding=None, bind_and_activate=True):
602
603 SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, allow_none,
604 encoding, bind_and_activate)
605 self.dispatchers = {}
606 self.allow_none = allow_none
Florent Xicluna3fa29f72011-10-30 20:18:50 +0100607 self.encoding = encoding or 'utf-8'
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000608
609 def add_dispatcher(self, path, dispatcher):
610 self.dispatchers[path] = dispatcher
611 return dispatcher
612
613 def get_dispatcher(self, path):
614 return self.dispatchers[path]
615
616 def _marshaled_dispatch(self, data, dispatch_method = None, path = None):
617 try:
618 response = self.dispatchers[path]._marshaled_dispatch(
619 data, dispatch_method, path)
620 except:
621 # report low level exception back to server
622 # (each dispatcher should have handled their own
623 # exceptions)
624 exc_type, exc_value = sys.exc_info()[:2]
Florent Xicluna3fa29f72011-10-30 20:18:50 +0100625 response = dumps(
626 Fault(1, "%s:%s" % (exc_type, exc_value)),
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000627 encoding=self.encoding, allow_none=self.allow_none)
Florent Xicluna3fa29f72011-10-30 20:18:50 +0100628 response = response.encode(self.encoding)
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000629 return response
630
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000631class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher):
632 """Simple handler for XML-RPC data passed through CGI."""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000633
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000634 def __init__(self, allow_none=False, encoding=None):
635 SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000636
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000637 def handle_xmlrpc(self, request_text):
638 """Handle a single XML-RPC request"""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000639
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000640 response = self._marshaled_dispatch(request_text)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000641
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000642 print('Content-Type: text/xml')
643 print('Content-Length: %d' % len(response))
644 print()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000645 sys.stdout.flush()
646 sys.stdout.buffer.write(response)
647 sys.stdout.buffer.flush()
Fredrik Lundhb329b712001-09-17 17:35:21 +0000648
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000649 def handle_get(self):
650 """Handle a single HTTP GET request.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000651
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000652 Default implementation indicates an error because
653 XML-RPC uses the POST method.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000654 """
655
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000656 code = 400
Georg Brandl24420152008-05-26 16:32:26 +0000657 message, explain = BaseHTTPRequestHandler.responses[code]
Tim Peters2c60f7a2003-01-29 03:49:43 +0000658
Georg Brandl24420152008-05-26 16:32:26 +0000659 response = http.server.DEFAULT_ERROR_MESSAGE % \
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000660 {
Tim Peters2c60f7a2003-01-29 03:49:43 +0000661 'code' : code,
662 'message' : message,
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000663 'explain' : explain
664 }
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000665 response = response.encode('utf-8')
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000666 print('Status: %d %s' % (code, message))
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000667 print('Content-Type: %s' % http.server.DEFAULT_ERROR_CONTENT_TYPE)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000668 print('Content-Length: %d' % len(response))
669 print()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000670 sys.stdout.flush()
671 sys.stdout.buffer.write(response)
672 sys.stdout.buffer.flush()
Tim Peters2c60f7a2003-01-29 03:49:43 +0000673
Georg Brandlfe991052009-09-16 15:54:04 +0000674 def handle_request(self, request_text=None):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000675 """Handle a single XML-RPC request passed through a CGI post method.
Tim Peters2c60f7a2003-01-29 03:49:43 +0000676
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000677 If no XML data is given then it is read from stdin. The resulting
678 XML-RPC response is printed to stdout along with the correct HTTP
679 headers.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000680 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000681
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000682 if request_text is None and \
683 os.environ.get('REQUEST_METHOD', None) == 'GET':
684 self.handle_get()
685 else:
686 # POST data is normally available through stdin
Georg Brandl99412e52009-04-01 04:27:47 +0000687 try:
688 length = int(os.environ.get('CONTENT_LENGTH', None))
Georg Brandlc7485062009-04-01 15:53:15 +0000689 except (ValueError, TypeError):
Georg Brandl99412e52009-04-01 04:27:47 +0000690 length = -1
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000691 if request_text is None:
Georg Brandl99412e52009-04-01 04:27:47 +0000692 request_text = sys.stdin.read(length)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000693
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000694 self.handle_xmlrpc(request_text)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000695
Georg Brandl38eceaa2008-05-26 11:14:17 +0000696
697# -----------------------------------------------------------------------------
698# Self documenting XML-RPC Server.
699
700class ServerHTMLDoc(pydoc.HTMLDoc):
701 """Class used to generate pydoc HTML document for a server"""
702
703 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
704 """Mark up some plain text, given a context of symbols to look for.
705 Each context dictionary maps object names to anchor names."""
706 escape = escape or self.escape
707 results = []
708 here = 0
709
710 # XXX Note that this regular expression does not allow for the
711 # hyperlinking of arbitrary strings being used as method
712 # names. Only methods with names consisting of word characters
713 # and '.'s are hyperlinked.
714 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
715 r'RFC[- ]?(\d+)|'
716 r'PEP[- ]?(\d+)|'
717 r'(self\.)?((?:\w|\.)+))\b')
718 while 1:
719 match = pattern.search(text, here)
720 if not match: break
721 start, end = match.span()
722 results.append(escape(text[here:start]))
723
724 all, scheme, rfc, pep, selfdot, name = match.groups()
725 if scheme:
726 url = escape(all).replace('"', '"')
727 results.append('<a href="%s">%s</a>' % (url, url))
728 elif rfc:
729 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
730 results.append('<a href="%s">%s</a>' % (url, escape(all)))
731 elif pep:
732 url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep)
733 results.append('<a href="%s">%s</a>' % (url, escape(all)))
734 elif text[end:end+1] == '(':
735 results.append(self.namelink(name, methods, funcs, classes))
736 elif selfdot:
737 results.append('self.<strong>%s</strong>' % name)
738 else:
739 results.append(self.namelink(name, classes))
740 here = end
741 results.append(escape(text[here:]))
742 return ''.join(results)
743
744 def docroutine(self, object, name, mod=None,
745 funcs={}, classes={}, methods={}, cl=None):
746 """Produce HTML documentation for a function or method object."""
747
748 anchor = (cl and cl.__name__ or '') + '-' + name
749 note = ''
750
751 title = '<a name="%s"><strong>%s</strong></a>' % (
752 self.escape(anchor), self.escape(name))
753
754 if inspect.ismethod(object):
755 args, varargs, varkw, defaults = inspect.getargspec(object)
756 # exclude the argument bound to the instance, it will be
757 # confusing to the non-Python user
758 argspec = inspect.formatargspec (
759 args[1:],
760 varargs,
761 varkw,
762 defaults,
763 formatvalue=self.formatvalue
764 )
765 elif inspect.isfunction(object):
766 args, varargs, varkw, defaults = inspect.getargspec(object)
767 argspec = inspect.formatargspec(
768 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
769 else:
770 argspec = '(...)'
771
772 if isinstance(object, tuple):
773 argspec = object[0] or argspec
774 docstring = object[1] or ""
775 else:
776 docstring = pydoc.getdoc(object)
777
778 decl = title + argspec + (note and self.grey(
779 '<font face="helvetica, arial">%s</font>' % note))
780
781 doc = self.markup(
782 docstring, self.preformat, funcs, classes, methods)
783 doc = doc and '<dd><tt>%s</tt></dd>' % doc
784 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
785
786 def docserver(self, server_name, package_documentation, methods):
787 """Produce HTML documentation for an XML-RPC server."""
788
789 fdict = {}
790 for key, value in methods.items():
791 fdict[key] = '#-' + key
792 fdict[value] = fdict[key]
793
794 server_name = self.escape(server_name)
795 head = '<big><big><strong>%s</strong></big></big>' % server_name
796 result = self.heading(head, '#ffffff', '#7799ee')
797
798 doc = self.markup(package_documentation, self.preformat, fdict)
799 doc = doc and '<tt>%s</tt>' % doc
800 result = result + '<p>%s</p>\n' % doc
801
802 contents = []
803 method_items = sorted(methods.items())
804 for key, value in method_items:
805 contents.append(self.docroutine(value, key, funcs=fdict))
806 result = result + self.bigsection(
807 'Methods', '#ffffff', '#eeaa77', ''.join(contents))
808
809 return result
810
811class XMLRPCDocGenerator:
812 """Generates documentation for an XML-RPC server.
813
814 This class is designed as mix-in and should not
815 be constructed directly.
816 """
817
818 def __init__(self):
819 # setup variables used for HTML documentation
820 self.server_name = 'XML-RPC Server Documentation'
821 self.server_documentation = \
822 "This server exports the following methods through the XML-RPC "\
823 "protocol."
824 self.server_title = 'XML-RPC Server Documentation'
825
826 def set_server_title(self, server_title):
827 """Set the HTML title of the generated server documentation"""
828
829 self.server_title = server_title
830
831 def set_server_name(self, server_name):
832 """Set the name of the generated HTML server documentation"""
833
834 self.server_name = server_name
835
836 def set_server_documentation(self, server_documentation):
837 """Set the documentation string for the entire server."""
838
839 self.server_documentation = server_documentation
840
841 def generate_html_documentation(self):
842 """generate_html_documentation() => html documentation for the server
843
844 Generates HTML documentation for the server using introspection for
845 installed functions and instances that do not implement the
846 _dispatch method. Alternatively, instances can choose to implement
847 the _get_method_argstring(method_name) method to provide the
848 argument string used in the documentation and the
849 _methodHelp(method_name) method to provide the help text used
850 in the documentation."""
851
852 methods = {}
853
854 for method_name in self.system_listMethods():
855 if method_name in self.funcs:
856 method = self.funcs[method_name]
857 elif self.instance is not None:
858 method_info = [None, None] # argspec, documentation
859 if hasattr(self.instance, '_get_method_argstring'):
860 method_info[0] = self.instance._get_method_argstring(method_name)
861 if hasattr(self.instance, '_methodHelp'):
862 method_info[1] = self.instance._methodHelp(method_name)
863
864 method_info = tuple(method_info)
865 if method_info != (None, None):
866 method = method_info
867 elif not hasattr(self.instance, '_dispatch'):
868 try:
869 method = resolve_dotted_attribute(
870 self.instance,
871 method_name
872 )
873 except AttributeError:
874 method = method_info
875 else:
876 method = method_info
877 else:
878 assert 0, "Could not find method in self.functions and no "\
879 "instance installed"
880
881 methods[method_name] = method
882
883 documenter = ServerHTMLDoc()
884 documentation = documenter.docserver(
885 self.server_name,
886 self.server_documentation,
887 methods
888 )
889
890 return documenter.page(self.server_title, documentation)
891
892class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
893 """XML-RPC and documentation request handler class.
894
895 Handles all HTTP POST requests and attempts to decode them as
896 XML-RPC requests.
897
898 Handles all HTTP GET requests and interprets them as requests
899 for documentation.
900 """
901
902 def do_GET(self):
903 """Handles the HTTP GET request.
904
905 Interpret all HTTP GET requests as requests for server
906 documentation.
907 """
908 # Check that the path is legal
909 if not self.is_rpc_path_valid():
910 self.report_404()
911 return
912
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000913 response = self.server.generate_html_documentation().encode('utf-8')
Georg Brandl38eceaa2008-05-26 11:14:17 +0000914 self.send_response(200)
915 self.send_header("Content-type", "text/html")
916 self.send_header("Content-length", str(len(response)))
917 self.end_headers()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000918 self.wfile.write(response)
Georg Brandl38eceaa2008-05-26 11:14:17 +0000919
Georg Brandl38eceaa2008-05-26 11:14:17 +0000920class DocXMLRPCServer( SimpleXMLRPCServer,
921 XMLRPCDocGenerator):
922 """XML-RPC and HTML documentation server.
923
924 Adds the ability to serve server documentation to the capabilities
925 of SimpleXMLRPCServer.
926 """
927
928 def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler,
Georg Brandlfe991052009-09-16 15:54:04 +0000929 logRequests=True, allow_none=False, encoding=None,
Georg Brandl38eceaa2008-05-26 11:14:17 +0000930 bind_and_activate=True):
931 SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests,
932 allow_none, encoding, bind_and_activate)
933 XMLRPCDocGenerator.__init__(self)
934
935class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler,
936 XMLRPCDocGenerator):
937 """Handler for XML-RPC data and documentation requests passed through
938 CGI"""
939
940 def handle_get(self):
941 """Handles the HTTP GET request.
942
943 Interpret all HTTP GET requests as requests for server
944 documentation.
945 """
946
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000947 response = self.generate_html_documentation().encode('utf-8')
Georg Brandl38eceaa2008-05-26 11:14:17 +0000948
949 print('Content-Type: text/html')
950 print('Content-Length: %d' % len(response))
951 print()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000952 sys.stdout.flush()
953 sys.stdout.buffer.write(response)
954 sys.stdout.buffer.flush()
Georg Brandl38eceaa2008-05-26 11:14:17 +0000955
956 def __init__(self):
957 CGIXMLRPCRequestHandler.__init__(self)
958 XMLRPCDocGenerator.__init__(self)
959
960
Fredrik Lundhb329b712001-09-17 17:35:21 +0000961if __name__ == '__main__':
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000962 print('Running XML-RPC server on port 8000')
Fredrik Lundhb329b712001-09-17 17:35:21 +0000963 server = SimpleXMLRPCServer(("localhost", 8000))
964 server.register_function(pow)
965 server.register_function(lambda x,y: x+y, 'add')
966 server.serve_forever()