blob: d914282917d1ac6cb400f20fa340cda33ac25919 [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
Florent Xicluna1b7458b2011-12-09 22:35:06 +0100163 def __init__(self, allow_none=False, encoding=None,
164 use_builtin_types=False):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000165 self.funcs = {}
166 self.instance = None
Andrew M. Kuchling10a16de2005-12-04 16:34:40 +0000167 self.allow_none = allow_none
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000168 self.encoding = encoding or 'utf-8'
Florent Xicluna1b7458b2011-12-09 22:35:06 +0100169 self.use_builtin_types = use_builtin_types
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000170
Guido van Rossumd0641422005-02-03 15:01:24 +0000171 def register_instance(self, instance, allow_dotted_names=False):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000172 """Registers an instance to respond to XML-RPC requests.
173
174 Only one instance can be installed at a time.
175
176 If the registered instance has a _dispatch method then that
177 method will be called with the name of the XML-RPC method and
Georg Brandl7eb4b7d2005-07-22 21:49:32 +0000178 its parameters as a tuple
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000179 e.g. instance._dispatch('add',(2,3))
180
181 If the registered instance does not have a _dispatch method
182 then the instance will be searched to find a matching method
183 and, if found, will be called. Methods beginning with an '_'
184 are considered private and will not be called by
185 SimpleXMLRPCServer.
186
187 If a registered function matches a XML-RPC request, then it
188 will be called instead of the registered instance.
Guido van Rossumd0641422005-02-03 15:01:24 +0000189
190 If the optional allow_dotted_names argument is true and the
191 instance does not have a _dispatch method, method names
192 containing dots are supported and resolved, as long as none of
193 the name segments start with an '_'.
194
195 *** SECURITY WARNING: ***
196
197 Enabling the allow_dotted_names options allows intruders
198 to access your module's global variables and may allow
199 intruders to execute arbitrary code on your machine. Only
200 use this option on a secure, closed network.
201
Fredrik Lundhb329b712001-09-17 17:35:21 +0000202 """
203
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000204 self.instance = instance
Guido van Rossumd0641422005-02-03 15:01:24 +0000205 self.allow_dotted_names = allow_dotted_names
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000206
Georg Brandlfe991052009-09-16 15:54:04 +0000207 def register_function(self, function, name=None):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000208 """Registers a function to respond to XML-RPC requests.
209
210 The optional name argument can be used to set a Unicode name
211 for the function.
212 """
213
214 if name is None:
215 name = function.__name__
216 self.funcs[name] = function
217
218 def register_introspection_functions(self):
219 """Registers the XML-RPC introspection methods in the system
220 namespace.
221
222 see http://xmlrpc.usefulinc.com/doc/reserved.html
223 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000224
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000225 self.funcs.update({'system.listMethods' : self.system_listMethods,
226 'system.methodSignature' : self.system_methodSignature,
227 'system.methodHelp' : self.system_methodHelp})
228
229 def register_multicall_functions(self):
230 """Registers the XML-RPC multicall method in the system
231 namespace.
232
233 see http://www.xmlrpc.com/discuss/msgReader$1208"""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000234
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000235 self.funcs.update({'system.multicall' : self.system_multicall})
Tim Peters2c60f7a2003-01-29 03:49:43 +0000236
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000237 def _marshaled_dispatch(self, data, dispatch_method = None, path = None):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000238 """Dispatches an XML-RPC method from marshalled (XML) data.
Tim Peters2c60f7a2003-01-29 03:49:43 +0000239
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000240 XML-RPC methods are dispatched from the marshalled (XML) data
241 using the _dispatch method and the result is returned as
242 marshalled data. For backwards compatibility, a dispatch
Tim Peters2c60f7a2003-01-29 03:49:43 +0000243 function can be provided as an argument (see comment in
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000244 SimpleXMLRPCRequestHandler.do_POST) but overriding the
Ezio Melotti13925002011-03-16 11:05:33 +0200245 existing method through subclassing is the preferred means
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000246 of changing method dispatch behavior.
247 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000248
Fredrik Lundhb329b712001-09-17 17:35:21 +0000249 try:
Florent Xicluna1b7458b2011-12-09 22:35:06 +0100250 params, method = loads(data, use_builtin_types=self.use_builtin_types)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000251
252 # generate response
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000253 if dispatch_method is not None:
254 response = dispatch_method(method, params)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000255 else:
Fredrik Lundhb329b712001-09-17 17:35:21 +0000256 response = self._dispatch(method, params)
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000257 # wrap response in a singleton tuple
258 response = (response,)
Georg Brandl38eceaa2008-05-26 11:14:17 +0000259 response = dumps(response, methodresponse=1,
260 allow_none=self.allow_none, encoding=self.encoding)
Guido van Rossumb940e112007-01-10 16:19:56 +0000261 except Fault as fault:
Georg Brandl38eceaa2008-05-26 11:14:17 +0000262 response = dumps(fault, allow_none=self.allow_none,
263 encoding=self.encoding)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000264 except:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000265 # report exception back to server
Thomas Wouters89f507f2006-12-13 04:49:30 +0000266 exc_type, exc_value, exc_tb = sys.exc_info()
Georg Brandl38eceaa2008-05-26 11:14:17 +0000267 response = dumps(
268 Fault(1, "%s:%s" % (exc_type, exc_value)),
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000269 encoding=self.encoding, allow_none=self.allow_none,
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000270 )
271
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000272 return response.encode(self.encoding)
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000273
274 def system_listMethods(self):
275 """system.listMethods() => ['add', 'subtract', 'multiple']
276
277 Returns a list of the methods supported by the server."""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000278
Hye-Shik Chang96042862007-08-19 10:49:11 +0000279 methods = set(self.funcs.keys())
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000280 if self.instance is not None:
281 # Instance can implement _listMethod to return a list of
282 # methods
283 if hasattr(self.instance, '_listMethods'):
Hye-Shik Chang96042862007-08-19 10:49:11 +0000284 methods |= set(self.instance._listMethods())
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000285 # if the instance has a _dispatch method then we
286 # don't have enough information to provide a list
287 # of methods
288 elif not hasattr(self.instance, '_dispatch'):
Hye-Shik Chang96042862007-08-19 10:49:11 +0000289 methods |= set(list_public_methods(self.instance))
290 return sorted(methods)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000291
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000292 def system_methodSignature(self, method_name):
293 """system.methodSignature('add') => [double, int, int]
294
Brett Cannonb9b5f162004-10-03 23:21:44 +0000295 Returns a list describing the signature of the method. In the
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000296 above example, the add method takes two integers as arguments
297 and returns a double result.
298
299 This server does NOT support system.methodSignature."""
300
301 # See http://xmlrpc.usefulinc.com/doc/sysmethodsig.html
Tim Peters2c60f7a2003-01-29 03:49:43 +0000302
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000303 return 'signatures not supported'
304
305 def system_methodHelp(self, method_name):
306 """system.methodHelp('add') => "Adds two integers together"
307
308 Returns a string containing documentation for the specified method."""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000309
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000310 method = None
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000311 if method_name in self.funcs:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000312 method = self.funcs[method_name]
313 elif self.instance is not None:
314 # Instance can implement _methodHelp to return help for a method
315 if hasattr(self.instance, '_methodHelp'):
316 return self.instance._methodHelp(method_name)
317 # if the instance has a _dispatch method then we
318 # don't have enough information to provide help
319 elif not hasattr(self.instance, '_dispatch'):
320 try:
321 method = resolve_dotted_attribute(
322 self.instance,
Guido van Rossumd0641422005-02-03 15:01:24 +0000323 method_name,
324 self.allow_dotted_names
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000325 )
326 except AttributeError:
327 pass
328
329 # Note that we aren't checking that the method actually
330 # be a callable object of some kind
331 if method is None:
332 return ""
Fredrik Lundhb329b712001-09-17 17:35:21 +0000333 else:
Neal Norwitz3f401f02003-06-29 04:19:37 +0000334 return pydoc.getdoc(method)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000335
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000336 def system_multicall(self, call_list):
337 """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \
338[[4], ...]
Fredrik Lundhb329b712001-09-17 17:35:21 +0000339
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000340 Allows the caller to package multiple XML-RPC calls into a single
341 request.
342
Tim Peters2c60f7a2003-01-29 03:49:43 +0000343 See http://www.xmlrpc.com/discuss/msgReader$1208
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000344 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000345
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000346 results = []
347 for call in call_list:
348 method_name = call['methodName']
349 params = call['params']
350
351 try:
352 # XXX A marshalling error in any response will fail the entire
353 # multicall. If someone cares they should fix this.
354 results.append([self._dispatch(method_name, params)])
Guido van Rossumb940e112007-01-10 16:19:56 +0000355 except Fault as fault:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000356 results.append(
357 {'faultCode' : fault.faultCode,
358 'faultString' : fault.faultString}
359 )
360 except:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000361 exc_type, exc_value, exc_tb = sys.exc_info()
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000362 results.append(
363 {'faultCode' : 1,
Thomas Wouters89f507f2006-12-13 04:49:30 +0000364 'faultString' : "%s:%s" % (exc_type, exc_value)}
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000365 )
366 return results
Tim Peters2c60f7a2003-01-29 03:49:43 +0000367
Fredrik Lundhb329b712001-09-17 17:35:21 +0000368 def _dispatch(self, method, params):
369 """Dispatches the XML-RPC method.
370
371 XML-RPC calls are forwarded to a registered function that
372 matches the called XML-RPC method name. If no such function
373 exists then the call is forwarded to the registered instance,
374 if available.
375
376 If the registered instance has a _dispatch method then that
377 method will be called with the name of the XML-RPC method and
Georg Brandl7eb4b7d2005-07-22 21:49:32 +0000378 its parameters as a tuple
Fredrik Lundhb329b712001-09-17 17:35:21 +0000379 e.g. instance._dispatch('add',(2,3))
380
381 If the registered instance does not have a _dispatch method
382 then the instance will be searched to find a matching method
383 and, if found, will be called.
384
385 Methods beginning with an '_' are considered private and will
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000386 not be called.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000387 """
388
Fredrik Lundhb329b712001-09-17 17:35:21 +0000389 func = None
390 try:
391 # check to see if a matching function has been registered
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000392 func = self.funcs[method]
Fredrik Lundhb329b712001-09-17 17:35:21 +0000393 except KeyError:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000394 if self.instance is not None:
Fredrik Lundhb329b712001-09-17 17:35:21 +0000395 # check for a _dispatch method
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000396 if hasattr(self.instance, '_dispatch'):
397 return self.instance._dispatch(method, params)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000398 else:
399 # call instance method directly
400 try:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000401 func = resolve_dotted_attribute(
402 self.instance,
Guido van Rossumd0641422005-02-03 15:01:24 +0000403 method,
404 self.allow_dotted_names
Fredrik Lundhb329b712001-09-17 17:35:21 +0000405 )
406 except AttributeError:
407 pass
408
409 if func is not None:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000410 return func(*params)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000411 else:
412 raise Exception('method "%s" is not supported' % method)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000413
Georg Brandl24420152008-05-26 16:32:26 +0000414class SimpleXMLRPCRequestHandler(BaseHTTPRequestHandler):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000415 """Simple XML-RPC request handler class.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000416
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000417 Handles all HTTP POST requests and attempts to decode them as
418 XML-RPC requests.
419 """
420
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000421 # Class attribute listing the accessible path components;
422 # paths not on this list will result in a 404 error.
423 rpc_paths = ('/', '/RPC2')
424
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000425 #if not None, encode responses larger than this, if possible
426 encode_threshold = 1400 #a common MTU
427
428 #Override form StreamRequestHandler: full buffering of output
429 #and no Nagle.
430 wbufsize = -1
431 disable_nagle_algorithm = True
432
433 # a re to match a gzip Accept-Encoding
434 aepattern = re.compile(r"""
435 \s* ([^\s;]+) \s* #content-coding
436 (;\s* q \s*=\s* ([0-9\.]+))? #q
437 """, re.VERBOSE | re.IGNORECASE)
438
439 def accept_encodings(self):
440 r = {}
441 ae = self.headers.get("Accept-Encoding", "")
442 for e in ae.split(","):
443 match = self.aepattern.match(e)
444 if match:
445 v = match.group(3)
446 v = float(v) if v else 1.0
447 r[match.group(1)] = v
448 return r
449
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000450 def is_rpc_path_valid(self):
451 if self.rpc_paths:
452 return self.path in self.rpc_paths
453 else:
454 # If .rpc_paths is empty, just assume all paths are legal
455 return True
456
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000457 def do_POST(self):
458 """Handles the HTTP POST request.
459
460 Attempts to interpret all HTTP POST requests as XML-RPC calls,
461 which are forwarded to the server's _dispatch method for handling.
462 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000463
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000464 # Check that the path is legal
465 if not self.is_rpc_path_valid():
466 self.report_404()
467 return
468
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000469 try:
Tim Peters536cf992005-12-25 23:18:31 +0000470 # Get arguments by reading body of request.
471 # We read this in chunks to avoid straining
Andrew M. Kuchlinge63fde72005-12-04 15:36:57 +0000472 # socket.read(); around the 10 or 15Mb mark, some platforms
473 # begin to have problems (bug #792570).
474 max_chunk_size = 10*1024*1024
475 size_remaining = int(self.headers["content-length"])
476 L = []
477 while size_remaining:
478 chunk_size = min(size_remaining, max_chunk_size)
Charles-François Nataliec1712a2012-02-18 14:42:57 +0100479 chunk = self.rfile.read(chunk_size)
480 if not chunk:
481 break
482 L.append(chunk)
Andrew M. Kuchlinge63fde72005-12-04 15:36:57 +0000483 size_remaining -= len(L[-1])
Hye-Shik Chang96042862007-08-19 10:49:11 +0000484 data = b''.join(L)
Andrew M. Kuchlinge63fde72005-12-04 15:36:57 +0000485
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000486 data = self.decode_request_content(data)
487 if data is None:
488 return #response has been sent
489
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000490 # In previous versions of SimpleXMLRPCServer, _dispatch
491 # could be overridden in this class, instead of in
492 # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
493 # check to see if a subclass implements _dispatch and dispatch
494 # using that method if present.
495 response = self.server._marshaled_dispatch(
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000496 data, getattr(self, '_dispatch', None), self.path
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000497 )
Guido van Rossum61e21b52007-08-20 19:06:03 +0000498 except Exception as e: # This should only happen if the module is buggy
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000499 # internal error, report as HTTP server error
500 self.send_response(500)
Guido van Rossum61e21b52007-08-20 19:06:03 +0000501
502 # Send information about the exception if requested
503 if hasattr(self.server, '_send_traceback_header') and \
504 self.server._send_traceback_header:
505 self.send_header("X-exception", str(e))
Victor Stinner5bfe1462010-04-16 13:28:05 +0000506 trace = traceback.format_exc()
507 trace = str(trace.encode('ASCII', 'backslashreplace'), 'ASCII')
508 self.send_header("X-traceback", trace)
Guido van Rossum61e21b52007-08-20 19:06:03 +0000509
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000510 self.send_header("Content-length", "0")
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000511 self.end_headers()
512 else:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000513 self.send_response(200)
514 self.send_header("Content-type", "text/xml")
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000515 if self.encode_threshold is not None:
516 if len(response) > self.encode_threshold:
517 q = self.accept_encodings().get("gzip", 0)
518 if q:
Kristján Valur Jónssonaefde242009-07-19 22:29:24 +0000519 try:
520 response = gzip_encode(response)
521 self.send_header("Content-Encoding", "gzip")
522 except NotImplementedError:
523 pass
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000524 self.send_header("Content-length", str(len(response)))
525 self.end_headers()
526 self.wfile.write(response)
527
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000528 def decode_request_content(self, data):
529 #support gzip encoding of request
530 encoding = self.headers.get("content-encoding", "identity").lower()
531 if encoding == "identity":
532 return data
533 if encoding == "gzip":
534 try:
535 return gzip_decode(data)
Kristján Valur Jónssonaefde242009-07-19 22:29:24 +0000536 except NotImplementedError:
537 self.send_response(501, "encoding %r not supported" % encoding)
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000538 except ValueError:
539 self.send_response(400, "error decoding gzip content")
540 else:
541 self.send_response(501, "encoding %r not supported" % encoding)
542 self.send_header("Content-length", "0")
543 self.end_headers()
Tim Peters2c60f7a2003-01-29 03:49:43 +0000544
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000545 def report_404 (self):
546 # Report a 404 error
547 self.send_response(404)
Christian Heimes0aa93cd2007-12-08 18:38:20 +0000548 response = b'No such page'
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000549 self.send_header("Content-type", "text/plain")
550 self.send_header("Content-length", str(len(response)))
551 self.end_headers()
552 self.wfile.write(response)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000553
Fredrik Lundhb329b712001-09-17 17:35:21 +0000554 def log_request(self, code='-', size='-'):
555 """Selectively log an accepted request."""
556
557 if self.server.logRequests:
Georg Brandl24420152008-05-26 16:32:26 +0000558 BaseHTTPRequestHandler.log_request(self, code, size)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000559
Alexandre Vassalottice261952008-05-12 02:31:37 +0000560class SimpleXMLRPCServer(socketserver.TCPServer,
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000561 SimpleXMLRPCDispatcher):
Fredrik Lundhb329b712001-09-17 17:35:21 +0000562 """Simple XML-RPC server.
563
564 Simple XML-RPC server that allows functions and a single instance
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000565 to be installed to handle requests. The default implementation
566 attempts to dispatch XML-RPC calls to the functions or instance
Florent Xiclunac4fec932011-10-30 20:19:32 +0100567 installed in the server. Override the _dispatch method inherited
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000568 from SimpleXMLRPCDispatcher to change this behavior.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000569 """
570
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000571 allow_reuse_address = True
572
Guido van Rossum61e21b52007-08-20 19:06:03 +0000573 # Warning: this is for debugging purposes only! Never set this to True in
574 # production code, as will be sending out sensitive information (exception
575 # and stack trace details) when exceptions are raised inside
576 # SimpleXMLRPCRequestHandler.do_POST
577 _send_traceback_header = False
578
Fredrik Lundhb329b712001-09-17 17:35:21 +0000579 def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
Florent Xicluna1b7458b2011-12-09 22:35:06 +0100580 logRequests=True, allow_none=False, encoding=None,
581 bind_and_activate=True, use_builtin_types=False):
Fredrik Lundhb329b712001-09-17 17:35:21 +0000582 self.logRequests = logRequests
Tim Peters2c60f7a2003-01-29 03:49:43 +0000583
Florent Xicluna1b7458b2011-12-09 22:35:06 +0100584 SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types)
Alexandre Vassalottice261952008-05-12 02:31:37 +0000585 socketserver.TCPServer.__init__(self, addr, requestHandler, bind_and_activate)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000586
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000587
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000588class MultiPathXMLRPCServer(SimpleXMLRPCServer):
589 """Multipath XML-RPC Server
590 This specialization of SimpleXMLRPCServer allows the user to create
591 multiple Dispatcher instances and assign them to different
592 HTTP request paths. This makes it possible to run two or more
593 'virtual XML-RPC servers' at the same port.
594 Make sure that the requestHandler accepts the paths in question.
595 """
596 def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
Florent Xicluna1b7458b2011-12-09 22:35:06 +0100597 logRequests=True, allow_none=False, encoding=None,
598 bind_and_activate=True, use_builtin_types=False):
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000599
600 SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, allow_none,
Florent Xicluna1b7458b2011-12-09 22:35:06 +0100601 encoding, bind_and_activate, use_builtin_types)
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000602 self.dispatchers = {}
603 self.allow_none = allow_none
Florent Xicluna3fa29f72011-10-30 20:18:50 +0100604 self.encoding = encoding or 'utf-8'
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000605
606 def add_dispatcher(self, path, dispatcher):
607 self.dispatchers[path] = dispatcher
608 return dispatcher
609
610 def get_dispatcher(self, path):
611 return self.dispatchers[path]
612
613 def _marshaled_dispatch(self, data, dispatch_method = None, path = None):
614 try:
615 response = self.dispatchers[path]._marshaled_dispatch(
616 data, dispatch_method, path)
617 except:
618 # report low level exception back to server
619 # (each dispatcher should have handled their own
620 # exceptions)
621 exc_type, exc_value = sys.exc_info()[:2]
Florent Xicluna3fa29f72011-10-30 20:18:50 +0100622 response = dumps(
623 Fault(1, "%s:%s" % (exc_type, exc_value)),
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000624 encoding=self.encoding, allow_none=self.allow_none)
Florent Xicluna3fa29f72011-10-30 20:18:50 +0100625 response = response.encode(self.encoding)
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000626 return response
627
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000628class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher):
629 """Simple handler for XML-RPC data passed through CGI."""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000630
Florent Xicluna1b7458b2011-12-09 22:35:06 +0100631 def __init__(self, allow_none=False, encoding=None, use_builtin_types=False):
632 SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000633
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000634 def handle_xmlrpc(self, request_text):
635 """Handle a single XML-RPC request"""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000636
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000637 response = self._marshaled_dispatch(request_text)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000638
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000639 print('Content-Type: text/xml')
640 print('Content-Length: %d' % len(response))
641 print()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000642 sys.stdout.flush()
643 sys.stdout.buffer.write(response)
644 sys.stdout.buffer.flush()
Fredrik Lundhb329b712001-09-17 17:35:21 +0000645
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000646 def handle_get(self):
647 """Handle a single HTTP GET request.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000648
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000649 Default implementation indicates an error because
650 XML-RPC uses the POST method.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000651 """
652
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000653 code = 400
Georg Brandl24420152008-05-26 16:32:26 +0000654 message, explain = BaseHTTPRequestHandler.responses[code]
Tim Peters2c60f7a2003-01-29 03:49:43 +0000655
Georg Brandl24420152008-05-26 16:32:26 +0000656 response = http.server.DEFAULT_ERROR_MESSAGE % \
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000657 {
Tim Peters2c60f7a2003-01-29 03:49:43 +0000658 'code' : code,
659 'message' : message,
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000660 'explain' : explain
661 }
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000662 response = response.encode('utf-8')
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000663 print('Status: %d %s' % (code, message))
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000664 print('Content-Type: %s' % http.server.DEFAULT_ERROR_CONTENT_TYPE)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000665 print('Content-Length: %d' % len(response))
666 print()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000667 sys.stdout.flush()
668 sys.stdout.buffer.write(response)
669 sys.stdout.buffer.flush()
Tim Peters2c60f7a2003-01-29 03:49:43 +0000670
Georg Brandlfe991052009-09-16 15:54:04 +0000671 def handle_request(self, request_text=None):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000672 """Handle a single XML-RPC request passed through a CGI post method.
Tim Peters2c60f7a2003-01-29 03:49:43 +0000673
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000674 If no XML data is given then it is read from stdin. The resulting
675 XML-RPC response is printed to stdout along with the correct HTTP
676 headers.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000677 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000678
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000679 if request_text is None and \
680 os.environ.get('REQUEST_METHOD', None) == 'GET':
681 self.handle_get()
682 else:
683 # POST data is normally available through stdin
Georg Brandl99412e52009-04-01 04:27:47 +0000684 try:
685 length = int(os.environ.get('CONTENT_LENGTH', None))
Georg Brandlc7485062009-04-01 15:53:15 +0000686 except (ValueError, TypeError):
Georg Brandl99412e52009-04-01 04:27:47 +0000687 length = -1
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000688 if request_text is None:
Georg Brandl99412e52009-04-01 04:27:47 +0000689 request_text = sys.stdin.read(length)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000690
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000691 self.handle_xmlrpc(request_text)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000692
Georg Brandl38eceaa2008-05-26 11:14:17 +0000693
694# -----------------------------------------------------------------------------
695# Self documenting XML-RPC Server.
696
697class ServerHTMLDoc(pydoc.HTMLDoc):
698 """Class used to generate pydoc HTML document for a server"""
699
700 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
701 """Mark up some plain text, given a context of symbols to look for.
702 Each context dictionary maps object names to anchor names."""
703 escape = escape or self.escape
704 results = []
705 here = 0
706
707 # XXX Note that this regular expression does not allow for the
708 # hyperlinking of arbitrary strings being used as method
709 # names. Only methods with names consisting of word characters
710 # and '.'s are hyperlinked.
711 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
712 r'RFC[- ]?(\d+)|'
713 r'PEP[- ]?(\d+)|'
714 r'(self\.)?((?:\w|\.)+))\b')
715 while 1:
716 match = pattern.search(text, here)
717 if not match: break
718 start, end = match.span()
719 results.append(escape(text[here:start]))
720
721 all, scheme, rfc, pep, selfdot, name = match.groups()
722 if scheme:
723 url = escape(all).replace('"', '"')
724 results.append('<a href="%s">%s</a>' % (url, url))
725 elif rfc:
726 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
727 results.append('<a href="%s">%s</a>' % (url, escape(all)))
728 elif pep:
729 url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep)
730 results.append('<a href="%s">%s</a>' % (url, escape(all)))
731 elif text[end:end+1] == '(':
732 results.append(self.namelink(name, methods, funcs, classes))
733 elif selfdot:
734 results.append('self.<strong>%s</strong>' % name)
735 else:
736 results.append(self.namelink(name, classes))
737 here = end
738 results.append(escape(text[here:]))
739 return ''.join(results)
740
741 def docroutine(self, object, name, mod=None,
742 funcs={}, classes={}, methods={}, cl=None):
743 """Produce HTML documentation for a function or method object."""
744
745 anchor = (cl and cl.__name__ or '') + '-' + name
746 note = ''
747
748 title = '<a name="%s"><strong>%s</strong></a>' % (
749 self.escape(anchor), self.escape(name))
750
751 if inspect.ismethod(object):
R David Murrayf22b62e2013-08-10 12:01:47 -0400752 args = inspect.getfullargspec(object)
Georg Brandl38eceaa2008-05-26 11:14:17 +0000753 # exclude the argument bound to the instance, it will be
754 # confusing to the non-Python user
755 argspec = inspect.formatargspec (
R David Murrayf22b62e2013-08-10 12:01:47 -0400756 args.args[1:],
757 args.varargs,
758 args.varkw,
759 args.defaults,
760 annotations=args.annotations,
Georg Brandl38eceaa2008-05-26 11:14:17 +0000761 formatvalue=self.formatvalue
762 )
763 elif inspect.isfunction(object):
R David Murrayf22b62e2013-08-10 12:01:47 -0400764 args = inspect.getfullargspec(object)
Georg Brandl38eceaa2008-05-26 11:14:17 +0000765 argspec = inspect.formatargspec(
R David Murrayf22b62e2013-08-10 12:01:47 -0400766 args.args, args.varargs, args.varkw, args.defaults,
767 annotations=args.annotations,
768 formatvalue=self.formatvalue)
Georg Brandl38eceaa2008-05-26 11:14:17 +0000769 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,
Florent Xicluna1b7458b2011-12-09 22:35:06 +0100930 bind_and_activate=True, use_builtin_types=False):
Georg Brandl38eceaa2008-05-26 11:14:17 +0000931 SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests,
Florent Xicluna1b7458b2011-12-09 22:35:06 +0100932 allow_none, encoding, bind_and_activate,
933 use_builtin_types)
Georg Brandl38eceaa2008-05-26 11:14:17 +0000934 XMLRPCDocGenerator.__init__(self)
935
936class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler,
937 XMLRPCDocGenerator):
938 """Handler for XML-RPC data and documentation requests passed through
939 CGI"""
940
941 def handle_get(self):
942 """Handles the HTTP GET request.
943
944 Interpret all HTTP GET requests as requests for server
945 documentation.
946 """
947
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000948 response = self.generate_html_documentation().encode('utf-8')
Georg Brandl38eceaa2008-05-26 11:14:17 +0000949
950 print('Content-Type: text/html')
951 print('Content-Length: %d' % len(response))
952 print()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000953 sys.stdout.flush()
954 sys.stdout.buffer.write(response)
955 sys.stdout.buffer.flush()
Georg Brandl38eceaa2008-05-26 11:14:17 +0000956
957 def __init__(self):
958 CGIXMLRPCRequestHandler.__init__(self)
959 XMLRPCDocGenerator.__init__(self)
960
961
Fredrik Lundhb329b712001-09-17 17:35:21 +0000962if __name__ == '__main__':
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')
Florent Xicluna75861df2011-10-30 20:39:24 +0100966 print('Serving XML-RPC on localhost port 8000')
967 try:
968 server.serve_forever()
969 except KeyboardInterrupt:
970 print("\nKeyboard interrupt received, exiting.")
971 server.server_close()
972 sys.exit(0)