blob: 9d93b1c7fb0f23408de1fe7f06301d4139ed8c6b [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
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 Norwitz732911f2003-06-29 04:16:28 +0000332 import pydoc
Neal Norwitz3f401f02003-06-29 04:19:37 +0000333 return pydoc.getdoc(method)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000334
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000335 def system_multicall(self, call_list):
336 """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \
337[[4], ...]
Fredrik Lundhb329b712001-09-17 17:35:21 +0000338
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000339 Allows the caller to package multiple XML-RPC calls into a single
340 request.
341
Tim Peters2c60f7a2003-01-29 03:49:43 +0000342 See http://www.xmlrpc.com/discuss/msgReader$1208
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000343 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000344
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000345 results = []
346 for call in call_list:
347 method_name = call['methodName']
348 params = call['params']
349
350 try:
351 # XXX A marshalling error in any response will fail the entire
352 # multicall. If someone cares they should fix this.
353 results.append([self._dispatch(method_name, params)])
Guido van Rossumb940e112007-01-10 16:19:56 +0000354 except Fault as fault:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000355 results.append(
356 {'faultCode' : fault.faultCode,
357 'faultString' : fault.faultString}
358 )
359 except:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000360 exc_type, exc_value, exc_tb = sys.exc_info()
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000361 results.append(
362 {'faultCode' : 1,
Thomas Wouters89f507f2006-12-13 04:49:30 +0000363 'faultString' : "%s:%s" % (exc_type, exc_value)}
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000364 )
365 return results
Tim Peters2c60f7a2003-01-29 03:49:43 +0000366
Fredrik Lundhb329b712001-09-17 17:35:21 +0000367 def _dispatch(self, method, params):
368 """Dispatches the XML-RPC method.
369
370 XML-RPC calls are forwarded to a registered function that
371 matches the called XML-RPC method name. If no such function
372 exists then the call is forwarded to the registered instance,
373 if available.
374
375 If the registered instance has a _dispatch method then that
376 method will be called with the name of the XML-RPC method and
Georg Brandl7eb4b7d2005-07-22 21:49:32 +0000377 its parameters as a tuple
Fredrik Lundhb329b712001-09-17 17:35:21 +0000378 e.g. instance._dispatch('add',(2,3))
379
380 If the registered instance does not have a _dispatch method
381 then the instance will be searched to find a matching method
382 and, if found, will be called.
383
384 Methods beginning with an '_' are considered private and will
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000385 not be called.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000386 """
387
Fredrik Lundhb329b712001-09-17 17:35:21 +0000388 func = None
389 try:
390 # check to see if a matching function has been registered
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000391 func = self.funcs[method]
Fredrik Lundhb329b712001-09-17 17:35:21 +0000392 except KeyError:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000393 if self.instance is not None:
Fredrik Lundhb329b712001-09-17 17:35:21 +0000394 # check for a _dispatch method
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000395 if hasattr(self.instance, '_dispatch'):
396 return self.instance._dispatch(method, params)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000397 else:
398 # call instance method directly
399 try:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000400 func = resolve_dotted_attribute(
401 self.instance,
Guido van Rossumd0641422005-02-03 15:01:24 +0000402 method,
403 self.allow_dotted_names
Fredrik Lundhb329b712001-09-17 17:35:21 +0000404 )
405 except AttributeError:
406 pass
407
408 if func is not None:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000409 return func(*params)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000410 else:
411 raise Exception('method "%s" is not supported' % method)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000412
Georg Brandl24420152008-05-26 16:32:26 +0000413class SimpleXMLRPCRequestHandler(BaseHTTPRequestHandler):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000414 """Simple XML-RPC request handler class.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000415
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000416 Handles all HTTP POST requests and attempts to decode them as
417 XML-RPC requests.
418 """
419
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000420 # Class attribute listing the accessible path components;
421 # paths not on this list will result in a 404 error.
422 rpc_paths = ('/', '/RPC2')
423
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000424 #if not None, encode responses larger than this, if possible
425 encode_threshold = 1400 #a common MTU
426
427 #Override form StreamRequestHandler: full buffering of output
428 #and no Nagle.
429 wbufsize = -1
430 disable_nagle_algorithm = True
431
432 # a re to match a gzip Accept-Encoding
433 aepattern = re.compile(r"""
434 \s* ([^\s;]+) \s* #content-coding
435 (;\s* q \s*=\s* ([0-9\.]+))? #q
436 """, re.VERBOSE | re.IGNORECASE)
437
438 def accept_encodings(self):
439 r = {}
440 ae = self.headers.get("Accept-Encoding", "")
441 for e in ae.split(","):
442 match = self.aepattern.match(e)
443 if match:
444 v = match.group(3)
445 v = float(v) if v else 1.0
446 r[match.group(1)] = v
447 return r
448
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000449 def is_rpc_path_valid(self):
450 if self.rpc_paths:
451 return self.path in self.rpc_paths
452 else:
453 # If .rpc_paths is empty, just assume all paths are legal
454 return True
455
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000456 def do_POST(self):
457 """Handles the HTTP POST request.
458
459 Attempts to interpret all HTTP POST requests as XML-RPC calls,
460 which are forwarded to the server's _dispatch method for handling.
461 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000462
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000463 # Check that the path is legal
464 if not self.is_rpc_path_valid():
465 self.report_404()
466 return
467
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000468 try:
Tim Peters536cf992005-12-25 23:18:31 +0000469 # Get arguments by reading body of request.
470 # We read this in chunks to avoid straining
Andrew M. Kuchlinge63fde72005-12-04 15:36:57 +0000471 # socket.read(); around the 10 or 15Mb mark, some platforms
472 # begin to have problems (bug #792570).
473 max_chunk_size = 10*1024*1024
474 size_remaining = int(self.headers["content-length"])
475 L = []
476 while size_remaining:
477 chunk_size = min(size_remaining, max_chunk_size)
478 L.append(self.rfile.read(chunk_size))
479 size_remaining -= len(L[-1])
Hye-Shik Chang96042862007-08-19 10:49:11 +0000480 data = b''.join(L)
Andrew M. Kuchlinge63fde72005-12-04 15:36:57 +0000481
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000482 data = self.decode_request_content(data)
483 if data is None:
484 return #response has been sent
485
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000486 # In previous versions of SimpleXMLRPCServer, _dispatch
487 # could be overridden in this class, instead of in
488 # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
489 # check to see if a subclass implements _dispatch and dispatch
490 # using that method if present.
491 response = self.server._marshaled_dispatch(
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000492 data, getattr(self, '_dispatch', None), self.path
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000493 )
Guido van Rossum61e21b52007-08-20 19:06:03 +0000494 except Exception as e: # This should only happen if the module is buggy
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000495 # internal error, report as HTTP server error
496 self.send_response(500)
Guido van Rossum61e21b52007-08-20 19:06:03 +0000497
498 # Send information about the exception if requested
499 if hasattr(self.server, '_send_traceback_header') and \
500 self.server._send_traceback_header:
501 self.send_header("X-exception", str(e))
Victor Stinner5bfe1462010-04-16 13:28:05 +0000502 trace = traceback.format_exc()
503 trace = str(trace.encode('ASCII', 'backslashreplace'), 'ASCII')
504 self.send_header("X-traceback", trace)
Guido van Rossum61e21b52007-08-20 19:06:03 +0000505
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000506 self.send_header("Content-length", "0")
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000507 self.end_headers()
508 else:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000509 self.send_response(200)
510 self.send_header("Content-type", "text/xml")
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000511 if self.encode_threshold is not None:
512 if len(response) > self.encode_threshold:
513 q = self.accept_encodings().get("gzip", 0)
514 if q:
Kristján Valur Jónssonaefde242009-07-19 22:29:24 +0000515 try:
516 response = gzip_encode(response)
517 self.send_header("Content-Encoding", "gzip")
518 except NotImplementedError:
519 pass
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000520 self.send_header("Content-length", str(len(response)))
521 self.end_headers()
522 self.wfile.write(response)
523
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000524 def decode_request_content(self, data):
525 #support gzip encoding of request
526 encoding = self.headers.get("content-encoding", "identity").lower()
527 if encoding == "identity":
528 return data
529 if encoding == "gzip":
530 try:
531 return gzip_decode(data)
Kristján Valur Jónssonaefde242009-07-19 22:29:24 +0000532 except NotImplementedError:
533 self.send_response(501, "encoding %r not supported" % encoding)
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000534 except ValueError:
535 self.send_response(400, "error decoding gzip content")
536 else:
537 self.send_response(501, "encoding %r not supported" % encoding)
538 self.send_header("Content-length", "0")
539 self.end_headers()
Tim Peters2c60f7a2003-01-29 03:49:43 +0000540
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000541 def report_404 (self):
542 # Report a 404 error
543 self.send_response(404)
Christian Heimes0aa93cd2007-12-08 18:38:20 +0000544 response = b'No such page'
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000545 self.send_header("Content-type", "text/plain")
546 self.send_header("Content-length", str(len(response)))
547 self.end_headers()
548 self.wfile.write(response)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000549
Fredrik Lundhb329b712001-09-17 17:35:21 +0000550 def log_request(self, code='-', size='-'):
551 """Selectively log an accepted request."""
552
553 if self.server.logRequests:
Georg Brandl24420152008-05-26 16:32:26 +0000554 BaseHTTPRequestHandler.log_request(self, code, size)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000555
Alexandre Vassalottice261952008-05-12 02:31:37 +0000556class SimpleXMLRPCServer(socketserver.TCPServer,
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000557 SimpleXMLRPCDispatcher):
Fredrik Lundhb329b712001-09-17 17:35:21 +0000558 """Simple XML-RPC server.
559
560 Simple XML-RPC server that allows functions and a single instance
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000561 to be installed to handle requests. The default implementation
562 attempts to dispatch XML-RPC calls to the functions or instance
563 installed in the server. Override the _dispatch method inhereted
564 from SimpleXMLRPCDispatcher to change this behavior.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000565 """
566
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000567 allow_reuse_address = True
568
Guido van Rossum61e21b52007-08-20 19:06:03 +0000569 # Warning: this is for debugging purposes only! Never set this to True in
570 # production code, as will be sending out sensitive information (exception
571 # and stack trace details) when exceptions are raised inside
572 # SimpleXMLRPCRequestHandler.do_POST
573 _send_traceback_header = False
574
Fredrik Lundhb329b712001-09-17 17:35:21 +0000575 def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
Guido van Rossumd8faa362007-04-27 19:54:29 +0000576 logRequests=True, allow_none=False, encoding=None, bind_and_activate=True):
Fredrik Lundhb329b712001-09-17 17:35:21 +0000577 self.logRequests = logRequests
Tim Peters2c60f7a2003-01-29 03:49:43 +0000578
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000579 SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
Alexandre Vassalottice261952008-05-12 02:31:37 +0000580 socketserver.TCPServer.__init__(self, addr, requestHandler, bind_and_activate)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000581
Tim Peters536cf992005-12-25 23:18:31 +0000582 # [Bug #1222790] If possible, set close-on-exec flag; if a
583 # method spawns a subprocess, the subprocess shouldn't have
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000584 # the listening socket open.
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000585 if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000586 flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
587 flags |= fcntl.FD_CLOEXEC
588 fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
589
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000590class MultiPathXMLRPCServer(SimpleXMLRPCServer):
591 """Multipath XML-RPC Server
592 This specialization of SimpleXMLRPCServer allows the user to create
593 multiple Dispatcher instances and assign them to different
594 HTTP request paths. This makes it possible to run two or more
595 'virtual XML-RPC servers' at the same port.
596 Make sure that the requestHandler accepts the paths in question.
597 """
598 def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
599 logRequests=True, allow_none=False, encoding=None, bind_and_activate=True):
600
601 SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, allow_none,
602 encoding, bind_and_activate)
603 self.dispatchers = {}
604 self.allow_none = allow_none
Florent Xicluna3fa29f72011-10-30 20:18:50 +0100605 self.encoding = encoding or 'utf-8'
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000606
607 def add_dispatcher(self, path, dispatcher):
608 self.dispatchers[path] = dispatcher
609 return dispatcher
610
611 def get_dispatcher(self, path):
612 return self.dispatchers[path]
613
614 def _marshaled_dispatch(self, data, dispatch_method = None, path = None):
615 try:
616 response = self.dispatchers[path]._marshaled_dispatch(
617 data, dispatch_method, path)
618 except:
619 # report low level exception back to server
620 # (each dispatcher should have handled their own
621 # exceptions)
622 exc_type, exc_value = sys.exc_info()[:2]
Florent Xicluna3fa29f72011-10-30 20:18:50 +0100623 response = dumps(
624 Fault(1, "%s:%s" % (exc_type, exc_value)),
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000625 encoding=self.encoding, allow_none=self.allow_none)
Florent Xicluna3fa29f72011-10-30 20:18:50 +0100626 response = response.encode(self.encoding)
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000627 return response
628
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000629class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher):
630 """Simple handler for XML-RPC data passed through CGI."""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000631
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000632 def __init__(self, allow_none=False, encoding=None):
633 SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000634
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000635 def handle_xmlrpc(self, request_text):
636 """Handle a single XML-RPC request"""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000637
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000638 response = self._marshaled_dispatch(request_text)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000639
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000640 print('Content-Type: text/xml')
641 print('Content-Length: %d' % len(response))
642 print()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000643 sys.stdout.flush()
644 sys.stdout.buffer.write(response)
645 sys.stdout.buffer.flush()
Fredrik Lundhb329b712001-09-17 17:35:21 +0000646
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000647 def handle_get(self):
648 """Handle a single HTTP GET request.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000649
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000650 Default implementation indicates an error because
651 XML-RPC uses the POST method.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000652 """
653
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000654 code = 400
Georg Brandl24420152008-05-26 16:32:26 +0000655 message, explain = BaseHTTPRequestHandler.responses[code]
Tim Peters2c60f7a2003-01-29 03:49:43 +0000656
Georg Brandl24420152008-05-26 16:32:26 +0000657 response = http.server.DEFAULT_ERROR_MESSAGE % \
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000658 {
Tim Peters2c60f7a2003-01-29 03:49:43 +0000659 'code' : code,
660 'message' : message,
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000661 'explain' : explain
662 }
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000663 response = response.encode('utf-8')
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000664 print('Status: %d %s' % (code, message))
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000665 print('Content-Type: %s' % http.server.DEFAULT_ERROR_CONTENT_TYPE)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000666 print('Content-Length: %d' % len(response))
667 print()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000668 sys.stdout.flush()
669 sys.stdout.buffer.write(response)
670 sys.stdout.buffer.flush()
Tim Peters2c60f7a2003-01-29 03:49:43 +0000671
Georg Brandlfe991052009-09-16 15:54:04 +0000672 def handle_request(self, request_text=None):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000673 """Handle a single XML-RPC request passed through a CGI post method.
Tim Peters2c60f7a2003-01-29 03:49:43 +0000674
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000675 If no XML data is given then it is read from stdin. The resulting
676 XML-RPC response is printed to stdout along with the correct HTTP
677 headers.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000678 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000679
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000680 if request_text is None and \
681 os.environ.get('REQUEST_METHOD', None) == 'GET':
682 self.handle_get()
683 else:
684 # POST data is normally available through stdin
Georg Brandl99412e52009-04-01 04:27:47 +0000685 try:
686 length = int(os.environ.get('CONTENT_LENGTH', None))
Georg Brandlc7485062009-04-01 15:53:15 +0000687 except (ValueError, TypeError):
Georg Brandl99412e52009-04-01 04:27:47 +0000688 length = -1
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000689 if request_text is None:
Georg Brandl99412e52009-04-01 04:27:47 +0000690 request_text = sys.stdin.read(length)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000691
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000692 self.handle_xmlrpc(request_text)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000693
Georg Brandl38eceaa2008-05-26 11:14:17 +0000694
695# -----------------------------------------------------------------------------
696# Self documenting XML-RPC Server.
697
698class ServerHTMLDoc(pydoc.HTMLDoc):
699 """Class used to generate pydoc HTML document for a server"""
700
701 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
702 """Mark up some plain text, given a context of symbols to look for.
703 Each context dictionary maps object names to anchor names."""
704 escape = escape or self.escape
705 results = []
706 here = 0
707
708 # XXX Note that this regular expression does not allow for the
709 # hyperlinking of arbitrary strings being used as method
710 # names. Only methods with names consisting of word characters
711 # and '.'s are hyperlinked.
712 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
713 r'RFC[- ]?(\d+)|'
714 r'PEP[- ]?(\d+)|'
715 r'(self\.)?((?:\w|\.)+))\b')
716 while 1:
717 match = pattern.search(text, here)
718 if not match: break
719 start, end = match.span()
720 results.append(escape(text[here:start]))
721
722 all, scheme, rfc, pep, selfdot, name = match.groups()
723 if scheme:
724 url = escape(all).replace('"', '"')
725 results.append('<a href="%s">%s</a>' % (url, url))
726 elif rfc:
727 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
728 results.append('<a href="%s">%s</a>' % (url, escape(all)))
729 elif pep:
730 url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep)
731 results.append('<a href="%s">%s</a>' % (url, escape(all)))
732 elif text[end:end+1] == '(':
733 results.append(self.namelink(name, methods, funcs, classes))
734 elif selfdot:
735 results.append('self.<strong>%s</strong>' % name)
736 else:
737 results.append(self.namelink(name, classes))
738 here = end
739 results.append(escape(text[here:]))
740 return ''.join(results)
741
742 def docroutine(self, object, name, mod=None,
743 funcs={}, classes={}, methods={}, cl=None):
744 """Produce HTML documentation for a function or method object."""
745
746 anchor = (cl and cl.__name__ or '') + '-' + name
747 note = ''
748
749 title = '<a name="%s"><strong>%s</strong></a>' % (
750 self.escape(anchor), self.escape(name))
751
752 if inspect.ismethod(object):
753 args, varargs, varkw, defaults = inspect.getargspec(object)
754 # exclude the argument bound to the instance, it will be
755 # confusing to the non-Python user
756 argspec = inspect.formatargspec (
757 args[1:],
758 varargs,
759 varkw,
760 defaults,
761 formatvalue=self.formatvalue
762 )
763 elif inspect.isfunction(object):
764 args, varargs, varkw, defaults = inspect.getargspec(object)
765 argspec = inspect.formatargspec(
766 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
767 else:
768 argspec = '(...)'
769
770 if isinstance(object, tuple):
771 argspec = object[0] or argspec
772 docstring = object[1] or ""
773 else:
774 docstring = pydoc.getdoc(object)
775
776 decl = title + argspec + (note and self.grey(
777 '<font face="helvetica, arial">%s</font>' % note))
778
779 doc = self.markup(
780 docstring, self.preformat, funcs, classes, methods)
781 doc = doc and '<dd><tt>%s</tt></dd>' % doc
782 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
783
784 def docserver(self, server_name, package_documentation, methods):
785 """Produce HTML documentation for an XML-RPC server."""
786
787 fdict = {}
788 for key, value in methods.items():
789 fdict[key] = '#-' + key
790 fdict[value] = fdict[key]
791
792 server_name = self.escape(server_name)
793 head = '<big><big><strong>%s</strong></big></big>' % server_name
794 result = self.heading(head, '#ffffff', '#7799ee')
795
796 doc = self.markup(package_documentation, self.preformat, fdict)
797 doc = doc and '<tt>%s</tt>' % doc
798 result = result + '<p>%s</p>\n' % doc
799
800 contents = []
801 method_items = sorted(methods.items())
802 for key, value in method_items:
803 contents.append(self.docroutine(value, key, funcs=fdict))
804 result = result + self.bigsection(
805 'Methods', '#ffffff', '#eeaa77', ''.join(contents))
806
807 return result
808
809class XMLRPCDocGenerator:
810 """Generates documentation for an XML-RPC server.
811
812 This class is designed as mix-in and should not
813 be constructed directly.
814 """
815
816 def __init__(self):
817 # setup variables used for HTML documentation
818 self.server_name = 'XML-RPC Server Documentation'
819 self.server_documentation = \
820 "This server exports the following methods through the XML-RPC "\
821 "protocol."
822 self.server_title = 'XML-RPC Server Documentation'
823
824 def set_server_title(self, server_title):
825 """Set the HTML title of the generated server documentation"""
826
827 self.server_title = server_title
828
829 def set_server_name(self, server_name):
830 """Set the name of the generated HTML server documentation"""
831
832 self.server_name = server_name
833
834 def set_server_documentation(self, server_documentation):
835 """Set the documentation string for the entire server."""
836
837 self.server_documentation = server_documentation
838
839 def generate_html_documentation(self):
840 """generate_html_documentation() => html documentation for the server
841
842 Generates HTML documentation for the server using introspection for
843 installed functions and instances that do not implement the
844 _dispatch method. Alternatively, instances can choose to implement
845 the _get_method_argstring(method_name) method to provide the
846 argument string used in the documentation and the
847 _methodHelp(method_name) method to provide the help text used
848 in the documentation."""
849
850 methods = {}
851
852 for method_name in self.system_listMethods():
853 if method_name in self.funcs:
854 method = self.funcs[method_name]
855 elif self.instance is not None:
856 method_info = [None, None] # argspec, documentation
857 if hasattr(self.instance, '_get_method_argstring'):
858 method_info[0] = self.instance._get_method_argstring(method_name)
859 if hasattr(self.instance, '_methodHelp'):
860 method_info[1] = self.instance._methodHelp(method_name)
861
862 method_info = tuple(method_info)
863 if method_info != (None, None):
864 method = method_info
865 elif not hasattr(self.instance, '_dispatch'):
866 try:
867 method = resolve_dotted_attribute(
868 self.instance,
869 method_name
870 )
871 except AttributeError:
872 method = method_info
873 else:
874 method = method_info
875 else:
876 assert 0, "Could not find method in self.functions and no "\
877 "instance installed"
878
879 methods[method_name] = method
880
881 documenter = ServerHTMLDoc()
882 documentation = documenter.docserver(
883 self.server_name,
884 self.server_documentation,
885 methods
886 )
887
888 return documenter.page(self.server_title, documentation)
889
890class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
891 """XML-RPC and documentation request handler class.
892
893 Handles all HTTP POST requests and attempts to decode them as
894 XML-RPC requests.
895
896 Handles all HTTP GET requests and interprets them as requests
897 for documentation.
898 """
899
900 def do_GET(self):
901 """Handles the HTTP GET request.
902
903 Interpret all HTTP GET requests as requests for server
904 documentation.
905 """
906 # Check that the path is legal
907 if not self.is_rpc_path_valid():
908 self.report_404()
909 return
910
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000911 response = self.server.generate_html_documentation().encode('utf-8')
Georg Brandl38eceaa2008-05-26 11:14:17 +0000912 self.send_response(200)
913 self.send_header("Content-type", "text/html")
914 self.send_header("Content-length", str(len(response)))
915 self.end_headers()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000916 self.wfile.write(response)
Georg Brandl38eceaa2008-05-26 11:14:17 +0000917
Georg Brandl38eceaa2008-05-26 11:14:17 +0000918class DocXMLRPCServer( SimpleXMLRPCServer,
919 XMLRPCDocGenerator):
920 """XML-RPC and HTML documentation server.
921
922 Adds the ability to serve server documentation to the capabilities
923 of SimpleXMLRPCServer.
924 """
925
926 def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler,
Georg Brandlfe991052009-09-16 15:54:04 +0000927 logRequests=True, allow_none=False, encoding=None,
Georg Brandl38eceaa2008-05-26 11:14:17 +0000928 bind_and_activate=True):
929 SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests,
930 allow_none, encoding, bind_and_activate)
931 XMLRPCDocGenerator.__init__(self)
932
933class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler,
934 XMLRPCDocGenerator):
935 """Handler for XML-RPC data and documentation requests passed through
936 CGI"""
937
938 def handle_get(self):
939 """Handles the HTTP GET request.
940
941 Interpret all HTTP GET requests as requests for server
942 documentation.
943 """
944
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000945 response = self.generate_html_documentation().encode('utf-8')
Georg Brandl38eceaa2008-05-26 11:14:17 +0000946
947 print('Content-Type: text/html')
948 print('Content-Length: %d' % len(response))
949 print()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000950 sys.stdout.flush()
951 sys.stdout.buffer.write(response)
952 sys.stdout.buffer.flush()
Georg Brandl38eceaa2008-05-26 11:14:17 +0000953
954 def __init__(self):
955 CGIXMLRPCRequestHandler.__init__(self)
956 XMLRPCDocGenerator.__init__(self)
957
958
Fredrik Lundhb329b712001-09-17 17:35:21 +0000959if __name__ == '__main__':
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000960 print('Running XML-RPC server on port 8000')
Fredrik Lundhb329b712001-09-17 17:35:21 +0000961 server = SimpleXMLRPCServer(("localhost", 8000))
962 server.register_function(pow)
963 server.register_function(lambda x,y: x+y, 'add')
964 server.serve_forever()