blob: bf22aa96b744c15c0f4705681b318dd275e951ec [file] [log] [blame]
Georg Brandl38eceaa2008-05-26 11:14:17 +00001"""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)
479 L.append(self.rfile.read(chunk_size))
480 size_remaining -= len(L[-1])
Hye-Shik Chang96042862007-08-19 10:49:11 +0000481 data = b''.join(L)
Andrew M. Kuchlinge63fde72005-12-04 15:36:57 +0000482
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000483 data = self.decode_request_content(data)
484 if data is None:
485 return #response has been sent
486
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000487 # In previous versions of SimpleXMLRPCServer, _dispatch
488 # could be overridden in this class, instead of in
489 # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
490 # check to see if a subclass implements _dispatch and dispatch
491 # using that method if present.
492 response = self.server._marshaled_dispatch(
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000493 data, getattr(self, '_dispatch', None), self.path
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000494 )
Guido van Rossum61e21b52007-08-20 19:06:03 +0000495 except Exception as e: # This should only happen if the module is buggy
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000496 # internal error, report as HTTP server error
497 self.send_response(500)
Guido van Rossum61e21b52007-08-20 19:06:03 +0000498
499 # Send information about the exception if requested
500 if hasattr(self.server, '_send_traceback_header') and \
501 self.server._send_traceback_header:
502 self.send_header("X-exception", str(e))
Victor Stinner5bfe1462010-04-16 13:28:05 +0000503 trace = traceback.format_exc()
504 trace = str(trace.encode('ASCII', 'backslashreplace'), 'ASCII')
505 self.send_header("X-traceback", trace)
Guido van Rossum61e21b52007-08-20 19:06:03 +0000506
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000507 self.send_header("Content-length", "0")
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000508 self.end_headers()
509 else:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000510 self.send_response(200)
511 self.send_header("Content-type", "text/xml")
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000512 if self.encode_threshold is not None:
513 if len(response) > self.encode_threshold:
514 q = self.accept_encodings().get("gzip", 0)
515 if q:
Kristján Valur Jónssonaefde242009-07-19 22:29:24 +0000516 try:
517 response = gzip_encode(response)
518 self.send_header("Content-Encoding", "gzip")
519 except NotImplementedError:
520 pass
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000521 self.send_header("Content-length", str(len(response)))
522 self.end_headers()
523 self.wfile.write(response)
524
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000525 def decode_request_content(self, data):
526 #support gzip encoding of request
527 encoding = self.headers.get("content-encoding", "identity").lower()
528 if encoding == "identity":
529 return data
530 if encoding == "gzip":
531 try:
532 return gzip_decode(data)
Kristján Valur Jónssonaefde242009-07-19 22:29:24 +0000533 except NotImplementedError:
534 self.send_response(501, "encoding %r not supported" % encoding)
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000535 except ValueError:
536 self.send_response(400, "error decoding gzip content")
537 else:
538 self.send_response(501, "encoding %r not supported" % encoding)
539 self.send_header("Content-length", "0")
540 self.end_headers()
Tim Peters2c60f7a2003-01-29 03:49:43 +0000541
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000542 def report_404 (self):
543 # Report a 404 error
544 self.send_response(404)
Christian Heimes0aa93cd2007-12-08 18:38:20 +0000545 response = b'No such page'
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000546 self.send_header("Content-type", "text/plain")
547 self.send_header("Content-length", str(len(response)))
548 self.end_headers()
549 self.wfile.write(response)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000550
Fredrik Lundhb329b712001-09-17 17:35:21 +0000551 def log_request(self, code='-', size='-'):
552 """Selectively log an accepted request."""
553
554 if self.server.logRequests:
Georg Brandl24420152008-05-26 16:32:26 +0000555 BaseHTTPRequestHandler.log_request(self, code, size)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000556
Alexandre Vassalottice261952008-05-12 02:31:37 +0000557class SimpleXMLRPCServer(socketserver.TCPServer,
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000558 SimpleXMLRPCDispatcher):
Fredrik Lundhb329b712001-09-17 17:35:21 +0000559 """Simple XML-RPC server.
560
561 Simple XML-RPC server that allows functions and a single instance
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000562 to be installed to handle requests. The default implementation
563 attempts to dispatch XML-RPC calls to the functions or instance
Florent Xiclunac4fec932011-10-30 20:19:32 +0100564 installed in the server. Override the _dispatch method inherited
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000565 from SimpleXMLRPCDispatcher to change this behavior.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000566 """
567
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000568 allow_reuse_address = True
569
Guido van Rossum61e21b52007-08-20 19:06:03 +0000570 # Warning: this is for debugging purposes only! Never set this to True in
571 # production code, as will be sending out sensitive information (exception
572 # and stack trace details) when exceptions are raised inside
573 # SimpleXMLRPCRequestHandler.do_POST
574 _send_traceback_header = False
575
Fredrik Lundhb329b712001-09-17 17:35:21 +0000576 def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
Florent Xicluna1b7458b2011-12-09 22:35:06 +0100577 logRequests=True, allow_none=False, encoding=None,
578 bind_and_activate=True, use_builtin_types=False):
Fredrik Lundhb329b712001-09-17 17:35:21 +0000579 self.logRequests = logRequests
Tim Peters2c60f7a2003-01-29 03:49:43 +0000580
Florent Xicluna1b7458b2011-12-09 22:35:06 +0100581 SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types)
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,
Florent Xicluna1b7458b2011-12-09 22:35:06 +0100601 logRequests=True, allow_none=False, encoding=None,
602 bind_and_activate=True, use_builtin_types=False):
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000603
604 SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, allow_none,
Florent Xicluna1b7458b2011-12-09 22:35:06 +0100605 encoding, bind_and_activate, use_builtin_types)
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000606 self.dispatchers = {}
607 self.allow_none = allow_none
Florent Xicluna3fa29f72011-10-30 20:18:50 +0100608 self.encoding = encoding or 'utf-8'
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000609
610 def add_dispatcher(self, path, dispatcher):
611 self.dispatchers[path] = dispatcher
612 return dispatcher
613
614 def get_dispatcher(self, path):
615 return self.dispatchers[path]
616
617 def _marshaled_dispatch(self, data, dispatch_method = None, path = None):
618 try:
619 response = self.dispatchers[path]._marshaled_dispatch(
620 data, dispatch_method, path)
621 except:
622 # report low level exception back to server
623 # (each dispatcher should have handled their own
624 # exceptions)
625 exc_type, exc_value = sys.exc_info()[:2]
Florent Xicluna3fa29f72011-10-30 20:18:50 +0100626 response = dumps(
627 Fault(1, "%s:%s" % (exc_type, exc_value)),
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000628 encoding=self.encoding, allow_none=self.allow_none)
Florent Xicluna3fa29f72011-10-30 20:18:50 +0100629 response = response.encode(self.encoding)
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000630 return response
631
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000632class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher):
633 """Simple handler for XML-RPC data passed through CGI."""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000634
Florent Xicluna1b7458b2011-12-09 22:35:06 +0100635 def __init__(self, allow_none=False, encoding=None, use_builtin_types=False):
636 SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000637
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000638 def handle_xmlrpc(self, request_text):
639 """Handle a single XML-RPC request"""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000640
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000641 response = self._marshaled_dispatch(request_text)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000642
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000643 print('Content-Type: text/xml')
644 print('Content-Length: %d' % len(response))
645 print()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000646 sys.stdout.flush()
647 sys.stdout.buffer.write(response)
648 sys.stdout.buffer.flush()
Fredrik Lundhb329b712001-09-17 17:35:21 +0000649
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000650 def handle_get(self):
651 """Handle a single HTTP GET request.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000652
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000653 Default implementation indicates an error because
654 XML-RPC uses the POST method.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000655 """
656
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000657 code = 400
Georg Brandl24420152008-05-26 16:32:26 +0000658 message, explain = BaseHTTPRequestHandler.responses[code]
Tim Peters2c60f7a2003-01-29 03:49:43 +0000659
Georg Brandl24420152008-05-26 16:32:26 +0000660 response = http.server.DEFAULT_ERROR_MESSAGE % \
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000661 {
Tim Peters2c60f7a2003-01-29 03:49:43 +0000662 'code' : code,
663 'message' : message,
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000664 'explain' : explain
665 }
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000666 response = response.encode('utf-8')
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000667 print('Status: %d %s' % (code, message))
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000668 print('Content-Type: %s' % http.server.DEFAULT_ERROR_CONTENT_TYPE)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000669 print('Content-Length: %d' % len(response))
670 print()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000671 sys.stdout.flush()
672 sys.stdout.buffer.write(response)
673 sys.stdout.buffer.flush()
Tim Peters2c60f7a2003-01-29 03:49:43 +0000674
Georg Brandlfe991052009-09-16 15:54:04 +0000675 def handle_request(self, request_text=None):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000676 """Handle a single XML-RPC request passed through a CGI post method.
Tim Peters2c60f7a2003-01-29 03:49:43 +0000677
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000678 If no XML data is given then it is read from stdin. The resulting
679 XML-RPC response is printed to stdout along with the correct HTTP
680 headers.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000681 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000682
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000683 if request_text is None and \
684 os.environ.get('REQUEST_METHOD', None) == 'GET':
685 self.handle_get()
686 else:
687 # POST data is normally available through stdin
Georg Brandl99412e52009-04-01 04:27:47 +0000688 try:
689 length = int(os.environ.get('CONTENT_LENGTH', None))
Georg Brandlc7485062009-04-01 15:53:15 +0000690 except (ValueError, TypeError):
Georg Brandl99412e52009-04-01 04:27:47 +0000691 length = -1
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000692 if request_text is None:
Georg Brandl99412e52009-04-01 04:27:47 +0000693 request_text = sys.stdin.read(length)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000694
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000695 self.handle_xmlrpc(request_text)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000696
Georg Brandl38eceaa2008-05-26 11:14:17 +0000697
698# -----------------------------------------------------------------------------
699# Self documenting XML-RPC Server.
700
701class ServerHTMLDoc(pydoc.HTMLDoc):
702 """Class used to generate pydoc HTML document for a server"""
703
704 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
705 """Mark up some plain text, given a context of symbols to look for.
706 Each context dictionary maps object names to anchor names."""
707 escape = escape or self.escape
708 results = []
709 here = 0
710
711 # XXX Note that this regular expression does not allow for the
712 # hyperlinking of arbitrary strings being used as method
713 # names. Only methods with names consisting of word characters
714 # and '.'s are hyperlinked.
715 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
716 r'RFC[- ]?(\d+)|'
717 r'PEP[- ]?(\d+)|'
718 r'(self\.)?((?:\w|\.)+))\b')
719 while 1:
720 match = pattern.search(text, here)
721 if not match: break
722 start, end = match.span()
723 results.append(escape(text[here:start]))
724
725 all, scheme, rfc, pep, selfdot, name = match.groups()
726 if scheme:
727 url = escape(all).replace('"', '"')
728 results.append('<a href="%s">%s</a>' % (url, url))
729 elif rfc:
730 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
731 results.append('<a href="%s">%s</a>' % (url, escape(all)))
732 elif pep:
733 url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep)
734 results.append('<a href="%s">%s</a>' % (url, escape(all)))
735 elif text[end:end+1] == '(':
736 results.append(self.namelink(name, methods, funcs, classes))
737 elif selfdot:
738 results.append('self.<strong>%s</strong>' % name)
739 else:
740 results.append(self.namelink(name, classes))
741 here = end
742 results.append(escape(text[here:]))
743 return ''.join(results)
744
745 def docroutine(self, object, name, mod=None,
746 funcs={}, classes={}, methods={}, cl=None):
747 """Produce HTML documentation for a function or method object."""
748
749 anchor = (cl and cl.__name__ or '') + '-' + name
750 note = ''
751
752 title = '<a name="%s"><strong>%s</strong></a>' % (
753 self.escape(anchor), self.escape(name))
754
755 if inspect.ismethod(object):
756 args, varargs, varkw, defaults = inspect.getargspec(object)
757 # exclude the argument bound to the instance, it will be
758 # confusing to the non-Python user
759 argspec = inspect.formatargspec (
760 args[1:],
761 varargs,
762 varkw,
763 defaults,
764 formatvalue=self.formatvalue
765 )
766 elif inspect.isfunction(object):
767 args, varargs, varkw, defaults = inspect.getargspec(object)
768 argspec = inspect.formatargspec(
769 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
770 else:
771 argspec = '(...)'
772
773 if isinstance(object, tuple):
774 argspec = object[0] or argspec
775 docstring = object[1] or ""
776 else:
777 docstring = pydoc.getdoc(object)
778
779 decl = title + argspec + (note and self.grey(
780 '<font face="helvetica, arial">%s</font>' % note))
781
782 doc = self.markup(
783 docstring, self.preformat, funcs, classes, methods)
784 doc = doc and '<dd><tt>%s</tt></dd>' % doc
785 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
786
787 def docserver(self, server_name, package_documentation, methods):
788 """Produce HTML documentation for an XML-RPC server."""
789
790 fdict = {}
791 for key, value in methods.items():
792 fdict[key] = '#-' + key
793 fdict[value] = fdict[key]
794
795 server_name = self.escape(server_name)
796 head = '<big><big><strong>%s</strong></big></big>' % server_name
797 result = self.heading(head, '#ffffff', '#7799ee')
798
799 doc = self.markup(package_documentation, self.preformat, fdict)
800 doc = doc and '<tt>%s</tt>' % doc
801 result = result + '<p>%s</p>\n' % doc
802
803 contents = []
804 method_items = sorted(methods.items())
805 for key, value in method_items:
806 contents.append(self.docroutine(value, key, funcs=fdict))
807 result = result + self.bigsection(
808 'Methods', '#ffffff', '#eeaa77', ''.join(contents))
809
810 return result
811
812class XMLRPCDocGenerator:
813 """Generates documentation for an XML-RPC server.
814
815 This class is designed as mix-in and should not
816 be constructed directly.
817 """
818
819 def __init__(self):
820 # setup variables used for HTML documentation
821 self.server_name = 'XML-RPC Server Documentation'
822 self.server_documentation = \
823 "This server exports the following methods through the XML-RPC "\
824 "protocol."
825 self.server_title = 'XML-RPC Server Documentation'
826
827 def set_server_title(self, server_title):
828 """Set the HTML title of the generated server documentation"""
829
830 self.server_title = server_title
831
832 def set_server_name(self, server_name):
833 """Set the name of the generated HTML server documentation"""
834
835 self.server_name = server_name
836
837 def set_server_documentation(self, server_documentation):
838 """Set the documentation string for the entire server."""
839
840 self.server_documentation = server_documentation
841
842 def generate_html_documentation(self):
843 """generate_html_documentation() => html documentation for the server
844
845 Generates HTML documentation for the server using introspection for
846 installed functions and instances that do not implement the
847 _dispatch method. Alternatively, instances can choose to implement
848 the _get_method_argstring(method_name) method to provide the
849 argument string used in the documentation and the
850 _methodHelp(method_name) method to provide the help text used
851 in the documentation."""
852
853 methods = {}
854
855 for method_name in self.system_listMethods():
856 if method_name in self.funcs:
857 method = self.funcs[method_name]
858 elif self.instance is not None:
859 method_info = [None, None] # argspec, documentation
860 if hasattr(self.instance, '_get_method_argstring'):
861 method_info[0] = self.instance._get_method_argstring(method_name)
862 if hasattr(self.instance, '_methodHelp'):
863 method_info[1] = self.instance._methodHelp(method_name)
864
865 method_info = tuple(method_info)
866 if method_info != (None, None):
867 method = method_info
868 elif not hasattr(self.instance, '_dispatch'):
869 try:
870 method = resolve_dotted_attribute(
871 self.instance,
872 method_name
873 )
874 except AttributeError:
875 method = method_info
876 else:
877 method = method_info
878 else:
879 assert 0, "Could not find method in self.functions and no "\
880 "instance installed"
881
882 methods[method_name] = method
883
884 documenter = ServerHTMLDoc()
885 documentation = documenter.docserver(
886 self.server_name,
887 self.server_documentation,
888 methods
889 )
890
891 return documenter.page(self.server_title, documentation)
892
893class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
894 """XML-RPC and documentation request handler class.
895
896 Handles all HTTP POST requests and attempts to decode them as
897 XML-RPC requests.
898
899 Handles all HTTP GET requests and interprets them as requests
900 for documentation.
901 """
902
903 def do_GET(self):
904 """Handles the HTTP GET request.
905
906 Interpret all HTTP GET requests as requests for server
907 documentation.
908 """
909 # Check that the path is legal
910 if not self.is_rpc_path_valid():
911 self.report_404()
912 return
913
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000914 response = self.server.generate_html_documentation().encode('utf-8')
Georg Brandl38eceaa2008-05-26 11:14:17 +0000915 self.send_response(200)
916 self.send_header("Content-type", "text/html")
917 self.send_header("Content-length", str(len(response)))
918 self.end_headers()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000919 self.wfile.write(response)
Georg Brandl38eceaa2008-05-26 11:14:17 +0000920
Georg Brandl38eceaa2008-05-26 11:14:17 +0000921class DocXMLRPCServer( SimpleXMLRPCServer,
922 XMLRPCDocGenerator):
923 """XML-RPC and HTML documentation server.
924
925 Adds the ability to serve server documentation to the capabilities
926 of SimpleXMLRPCServer.
927 """
928
929 def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler,
Georg Brandlfe991052009-09-16 15:54:04 +0000930 logRequests=True, allow_none=False, encoding=None,
Florent Xicluna1b7458b2011-12-09 22:35:06 +0100931 bind_and_activate=True, use_builtin_types=False):
Georg Brandl38eceaa2008-05-26 11:14:17 +0000932 SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests,
Florent Xicluna1b7458b2011-12-09 22:35:06 +0100933 allow_none, encoding, bind_and_activate,
934 use_builtin_types)
Georg Brandl38eceaa2008-05-26 11:14:17 +0000935 XMLRPCDocGenerator.__init__(self)
936
937class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler,
938 XMLRPCDocGenerator):
939 """Handler for XML-RPC data and documentation requests passed through
940 CGI"""
941
942 def handle_get(self):
943 """Handles the HTTP GET request.
944
945 Interpret all HTTP GET requests as requests for server
946 documentation.
947 """
948
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000949 response = self.generate_html_documentation().encode('utf-8')
Georg Brandl38eceaa2008-05-26 11:14:17 +0000950
951 print('Content-Type: text/html')
952 print('Content-Length: %d' % len(response))
953 print()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000954 sys.stdout.flush()
955 sys.stdout.buffer.write(response)
956 sys.stdout.buffer.flush()
Georg Brandl38eceaa2008-05-26 11:14:17 +0000957
958 def __init__(self):
959 CGIXMLRPCRequestHandler.__init__(self)
960 XMLRPCDocGenerator.__init__(self)
961
962
Fredrik Lundhb329b712001-09-17 17:35:21 +0000963if __name__ == '__main__':
Fredrik Lundhb329b712001-09-17 17:35:21 +0000964 server = SimpleXMLRPCServer(("localhost", 8000))
965 server.register_function(pow)
966 server.register_function(lambda x,y: x+y, 'add')
Florent Xicluna75861df2011-10-30 20:39:24 +0100967 print('Serving XML-RPC on localhost port 8000')
968 try:
969 server.serve_forever()
970 except KeyboardInterrupt:
971 print("\nKeyboard interrupt received, exiting.")
972 server.server_close()
973 sys.exit(0)