blob: 72f3bfc1048b3005670fded42fc9dfaafcc2dd93 [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 Norwitz3f401f02003-06-29 04:19:37 +0000332 return pydoc.getdoc(method)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000333
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000334 def system_multicall(self, call_list):
335 """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \
336[[4], ...]
Fredrik Lundhb329b712001-09-17 17:35:21 +0000337
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000338 Allows the caller to package multiple XML-RPC calls into a single
339 request.
340
Tim Peters2c60f7a2003-01-29 03:49:43 +0000341 See http://www.xmlrpc.com/discuss/msgReader$1208
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000342 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000343
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000344 results = []
345 for call in call_list:
346 method_name = call['methodName']
347 params = call['params']
348
349 try:
350 # XXX A marshalling error in any response will fail the entire
351 # multicall. If someone cares they should fix this.
352 results.append([self._dispatch(method_name, params)])
Guido van Rossumb940e112007-01-10 16:19:56 +0000353 except Fault as fault:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000354 results.append(
355 {'faultCode' : fault.faultCode,
356 'faultString' : fault.faultString}
357 )
358 except:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000359 exc_type, exc_value, exc_tb = sys.exc_info()
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000360 results.append(
361 {'faultCode' : 1,
Thomas Wouters89f507f2006-12-13 04:49:30 +0000362 'faultString' : "%s:%s" % (exc_type, exc_value)}
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000363 )
364 return results
Tim Peters2c60f7a2003-01-29 03:49:43 +0000365
Fredrik Lundhb329b712001-09-17 17:35:21 +0000366 def _dispatch(self, method, params):
367 """Dispatches the XML-RPC method.
368
369 XML-RPC calls are forwarded to a registered function that
370 matches the called XML-RPC method name. If no such function
371 exists then the call is forwarded to the registered instance,
372 if available.
373
374 If the registered instance has a _dispatch method then that
375 method will be called with the name of the XML-RPC method and
Georg Brandl7eb4b7d2005-07-22 21:49:32 +0000376 its parameters as a tuple
Fredrik Lundhb329b712001-09-17 17:35:21 +0000377 e.g. instance._dispatch('add',(2,3))
378
379 If the registered instance does not have a _dispatch method
380 then the instance will be searched to find a matching method
381 and, if found, will be called.
382
383 Methods beginning with an '_' are considered private and will
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000384 not be called.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000385 """
386
Fredrik Lundhb329b712001-09-17 17:35:21 +0000387 func = None
388 try:
389 # check to see if a matching function has been registered
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000390 func = self.funcs[method]
Fredrik Lundhb329b712001-09-17 17:35:21 +0000391 except KeyError:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000392 if self.instance is not None:
Fredrik Lundhb329b712001-09-17 17:35:21 +0000393 # check for a _dispatch method
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000394 if hasattr(self.instance, '_dispatch'):
395 return self.instance._dispatch(method, params)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000396 else:
397 # call instance method directly
398 try:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000399 func = resolve_dotted_attribute(
400 self.instance,
Guido van Rossumd0641422005-02-03 15:01:24 +0000401 method,
402 self.allow_dotted_names
Fredrik Lundhb329b712001-09-17 17:35:21 +0000403 )
404 except AttributeError:
405 pass
406
407 if func is not None:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000408 return func(*params)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000409 else:
410 raise Exception('method "%s" is not supported' % method)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000411
Georg Brandl24420152008-05-26 16:32:26 +0000412class SimpleXMLRPCRequestHandler(BaseHTTPRequestHandler):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000413 """Simple XML-RPC request handler class.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000414
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000415 Handles all HTTP POST requests and attempts to decode them as
416 XML-RPC requests.
417 """
418
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000419 # Class attribute listing the accessible path components;
420 # paths not on this list will result in a 404 error.
421 rpc_paths = ('/', '/RPC2')
422
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000423 #if not None, encode responses larger than this, if possible
424 encode_threshold = 1400 #a common MTU
425
426 #Override form StreamRequestHandler: full buffering of output
427 #and no Nagle.
428 wbufsize = -1
429 disable_nagle_algorithm = True
430
431 # a re to match a gzip Accept-Encoding
432 aepattern = re.compile(r"""
433 \s* ([^\s;]+) \s* #content-coding
434 (;\s* q \s*=\s* ([0-9\.]+))? #q
435 """, re.VERBOSE | re.IGNORECASE)
436
437 def accept_encodings(self):
438 r = {}
439 ae = self.headers.get("Accept-Encoding", "")
440 for e in ae.split(","):
441 match = self.aepattern.match(e)
442 if match:
443 v = match.group(3)
444 v = float(v) if v else 1.0
445 r[match.group(1)] = v
446 return r
447
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000448 def is_rpc_path_valid(self):
449 if self.rpc_paths:
450 return self.path in self.rpc_paths
451 else:
452 # If .rpc_paths is empty, just assume all paths are legal
453 return True
454
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000455 def do_POST(self):
456 """Handles the HTTP POST request.
457
458 Attempts to interpret all HTTP POST requests as XML-RPC calls,
459 which are forwarded to the server's _dispatch method for handling.
460 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000461
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000462 # Check that the path is legal
463 if not self.is_rpc_path_valid():
464 self.report_404()
465 return
466
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000467 try:
Tim Peters536cf992005-12-25 23:18:31 +0000468 # Get arguments by reading body of request.
469 # We read this in chunks to avoid straining
Andrew M. Kuchlinge63fde72005-12-04 15:36:57 +0000470 # socket.read(); around the 10 or 15Mb mark, some platforms
471 # begin to have problems (bug #792570).
472 max_chunk_size = 10*1024*1024
473 size_remaining = int(self.headers["content-length"])
474 L = []
475 while size_remaining:
476 chunk_size = min(size_remaining, max_chunk_size)
477 L.append(self.rfile.read(chunk_size))
478 size_remaining -= len(L[-1])
Hye-Shik Chang96042862007-08-19 10:49:11 +0000479 data = b''.join(L)
Andrew M. Kuchlinge63fde72005-12-04 15:36:57 +0000480
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000481 data = self.decode_request_content(data)
482 if data is None:
483 return #response has been sent
484
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000485 # In previous versions of SimpleXMLRPCServer, _dispatch
486 # could be overridden in this class, instead of in
487 # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
488 # check to see if a subclass implements _dispatch and dispatch
489 # using that method if present.
490 response = self.server._marshaled_dispatch(
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000491 data, getattr(self, '_dispatch', None), self.path
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000492 )
Guido van Rossum61e21b52007-08-20 19:06:03 +0000493 except Exception as e: # This should only happen if the module is buggy
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000494 # internal error, report as HTTP server error
495 self.send_response(500)
Guido van Rossum61e21b52007-08-20 19:06:03 +0000496
497 # Send information about the exception if requested
498 if hasattr(self.server, '_send_traceback_header') and \
499 self.server._send_traceback_header:
500 self.send_header("X-exception", str(e))
Victor Stinner5bfe1462010-04-16 13:28:05 +0000501 trace = traceback.format_exc()
502 trace = str(trace.encode('ASCII', 'backslashreplace'), 'ASCII')
503 self.send_header("X-traceback", trace)
Guido van Rossum61e21b52007-08-20 19:06:03 +0000504
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000505 self.send_header("Content-length", "0")
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000506 self.end_headers()
507 else:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000508 self.send_response(200)
509 self.send_header("Content-type", "text/xml")
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000510 if self.encode_threshold is not None:
511 if len(response) > self.encode_threshold:
512 q = self.accept_encodings().get("gzip", 0)
513 if q:
Kristján Valur Jónssonaefde242009-07-19 22:29:24 +0000514 try:
515 response = gzip_encode(response)
516 self.send_header("Content-Encoding", "gzip")
517 except NotImplementedError:
518 pass
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000519 self.send_header("Content-length", str(len(response)))
520 self.end_headers()
521 self.wfile.write(response)
522
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000523 def decode_request_content(self, data):
524 #support gzip encoding of request
525 encoding = self.headers.get("content-encoding", "identity").lower()
526 if encoding == "identity":
527 return data
528 if encoding == "gzip":
529 try:
530 return gzip_decode(data)
Kristján Valur Jónssonaefde242009-07-19 22:29:24 +0000531 except NotImplementedError:
532 self.send_response(501, "encoding %r not supported" % encoding)
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000533 except ValueError:
534 self.send_response(400, "error decoding gzip content")
535 else:
536 self.send_response(501, "encoding %r not supported" % encoding)
537 self.send_header("Content-length", "0")
538 self.end_headers()
Tim Peters2c60f7a2003-01-29 03:49:43 +0000539
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000540 def report_404 (self):
541 # Report a 404 error
542 self.send_response(404)
Christian Heimes0aa93cd2007-12-08 18:38:20 +0000543 response = b'No such page'
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000544 self.send_header("Content-type", "text/plain")
545 self.send_header("Content-length", str(len(response)))
546 self.end_headers()
547 self.wfile.write(response)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000548
Fredrik Lundhb329b712001-09-17 17:35:21 +0000549 def log_request(self, code='-', size='-'):
550 """Selectively log an accepted request."""
551
552 if self.server.logRequests:
Georg Brandl24420152008-05-26 16:32:26 +0000553 BaseHTTPRequestHandler.log_request(self, code, size)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000554
Alexandre Vassalottice261952008-05-12 02:31:37 +0000555class SimpleXMLRPCServer(socketserver.TCPServer,
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000556 SimpleXMLRPCDispatcher):
Fredrik Lundhb329b712001-09-17 17:35:21 +0000557 """Simple XML-RPC server.
558
559 Simple XML-RPC server that allows functions and a single instance
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000560 to be installed to handle requests. The default implementation
561 attempts to dispatch XML-RPC calls to the functions or instance
Florent Xiclunac4fec932011-10-30 20:19:32 +0100562 installed in the server. Override the _dispatch method inherited
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000563 from SimpleXMLRPCDispatcher to change this behavior.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000564 """
565
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000566 allow_reuse_address = True
567
Guido van Rossum61e21b52007-08-20 19:06:03 +0000568 # Warning: this is for debugging purposes only! Never set this to True in
569 # production code, as will be sending out sensitive information (exception
570 # and stack trace details) when exceptions are raised inside
571 # SimpleXMLRPCRequestHandler.do_POST
572 _send_traceback_header = False
573
Fredrik Lundhb329b712001-09-17 17:35:21 +0000574 def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
Guido van Rossumd8faa362007-04-27 19:54:29 +0000575 logRequests=True, allow_none=False, encoding=None, bind_and_activate=True):
Fredrik Lundhb329b712001-09-17 17:35:21 +0000576 self.logRequests = logRequests
Tim Peters2c60f7a2003-01-29 03:49:43 +0000577
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000578 SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
Alexandre Vassalottice261952008-05-12 02:31:37 +0000579 socketserver.TCPServer.__init__(self, addr, requestHandler, bind_and_activate)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000580
Tim Peters536cf992005-12-25 23:18:31 +0000581 # [Bug #1222790] If possible, set close-on-exec flag; if a
582 # method spawns a subprocess, the subprocess shouldn't have
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000583 # the listening socket open.
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000584 if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000585 flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
586 flags |= fcntl.FD_CLOEXEC
587 fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
588
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000589class MultiPathXMLRPCServer(SimpleXMLRPCServer):
590 """Multipath XML-RPC Server
591 This specialization of SimpleXMLRPCServer allows the user to create
592 multiple Dispatcher instances and assign them to different
593 HTTP request paths. This makes it possible to run two or more
594 'virtual XML-RPC servers' at the same port.
595 Make sure that the requestHandler accepts the paths in question.
596 """
597 def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
598 logRequests=True, allow_none=False, encoding=None, bind_and_activate=True):
599
600 SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, allow_none,
601 encoding, bind_and_activate)
602 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
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000631 def __init__(self, allow_none=False, encoding=None):
632 SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
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):
752 args, varargs, varkw, defaults = inspect.getargspec(object)
753 # exclude the argument bound to the instance, it will be
754 # confusing to the non-Python user
755 argspec = inspect.formatargspec (
756 args[1:],
757 varargs,
758 varkw,
759 defaults,
760 formatvalue=self.formatvalue
761 )
762 elif inspect.isfunction(object):
763 args, varargs, varkw, defaults = inspect.getargspec(object)
764 argspec = inspect.formatargspec(
765 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
766 else:
767 argspec = '(...)'
768
769 if isinstance(object, tuple):
770 argspec = object[0] or argspec
771 docstring = object[1] or ""
772 else:
773 docstring = pydoc.getdoc(object)
774
775 decl = title + argspec + (note and self.grey(
776 '<font face="helvetica, arial">%s</font>' % note))
777
778 doc = self.markup(
779 docstring, self.preformat, funcs, classes, methods)
780 doc = doc and '<dd><tt>%s</tt></dd>' % doc
781 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
782
783 def docserver(self, server_name, package_documentation, methods):
784 """Produce HTML documentation for an XML-RPC server."""
785
786 fdict = {}
787 for key, value in methods.items():
788 fdict[key] = '#-' + key
789 fdict[value] = fdict[key]
790
791 server_name = self.escape(server_name)
792 head = '<big><big><strong>%s</strong></big></big>' % server_name
793 result = self.heading(head, '#ffffff', '#7799ee')
794
795 doc = self.markup(package_documentation, self.preformat, fdict)
796 doc = doc and '<tt>%s</tt>' % doc
797 result = result + '<p>%s</p>\n' % doc
798
799 contents = []
800 method_items = sorted(methods.items())
801 for key, value in method_items:
802 contents.append(self.docroutine(value, key, funcs=fdict))
803 result = result + self.bigsection(
804 'Methods', '#ffffff', '#eeaa77', ''.join(contents))
805
806 return result
807
808class XMLRPCDocGenerator:
809 """Generates documentation for an XML-RPC server.
810
811 This class is designed as mix-in and should not
812 be constructed directly.
813 """
814
815 def __init__(self):
816 # setup variables used for HTML documentation
817 self.server_name = 'XML-RPC Server Documentation'
818 self.server_documentation = \
819 "This server exports the following methods through the XML-RPC "\
820 "protocol."
821 self.server_title = 'XML-RPC Server Documentation'
822
823 def set_server_title(self, server_title):
824 """Set the HTML title of the generated server documentation"""
825
826 self.server_title = server_title
827
828 def set_server_name(self, server_name):
829 """Set the name of the generated HTML server documentation"""
830
831 self.server_name = server_name
832
833 def set_server_documentation(self, server_documentation):
834 """Set the documentation string for the entire server."""
835
836 self.server_documentation = server_documentation
837
838 def generate_html_documentation(self):
839 """generate_html_documentation() => html documentation for the server
840
841 Generates HTML documentation for the server using introspection for
842 installed functions and instances that do not implement the
843 _dispatch method. Alternatively, instances can choose to implement
844 the _get_method_argstring(method_name) method to provide the
845 argument string used in the documentation and the
846 _methodHelp(method_name) method to provide the help text used
847 in the documentation."""
848
849 methods = {}
850
851 for method_name in self.system_listMethods():
852 if method_name in self.funcs:
853 method = self.funcs[method_name]
854 elif self.instance is not None:
855 method_info = [None, None] # argspec, documentation
856 if hasattr(self.instance, '_get_method_argstring'):
857 method_info[0] = self.instance._get_method_argstring(method_name)
858 if hasattr(self.instance, '_methodHelp'):
859 method_info[1] = self.instance._methodHelp(method_name)
860
861 method_info = tuple(method_info)
862 if method_info != (None, None):
863 method = method_info
864 elif not hasattr(self.instance, '_dispatch'):
865 try:
866 method = resolve_dotted_attribute(
867 self.instance,
868 method_name
869 )
870 except AttributeError:
871 method = method_info
872 else:
873 method = method_info
874 else:
875 assert 0, "Could not find method in self.functions and no "\
876 "instance installed"
877
878 methods[method_name] = method
879
880 documenter = ServerHTMLDoc()
881 documentation = documenter.docserver(
882 self.server_name,
883 self.server_documentation,
884 methods
885 )
886
887 return documenter.page(self.server_title, documentation)
888
889class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
890 """XML-RPC and documentation request handler class.
891
892 Handles all HTTP POST requests and attempts to decode them as
893 XML-RPC requests.
894
895 Handles all HTTP GET requests and interprets them as requests
896 for documentation.
897 """
898
899 def do_GET(self):
900 """Handles the HTTP GET request.
901
902 Interpret all HTTP GET requests as requests for server
903 documentation.
904 """
905 # Check that the path is legal
906 if not self.is_rpc_path_valid():
907 self.report_404()
908 return
909
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000910 response = self.server.generate_html_documentation().encode('utf-8')
Georg Brandl38eceaa2008-05-26 11:14:17 +0000911 self.send_response(200)
912 self.send_header("Content-type", "text/html")
913 self.send_header("Content-length", str(len(response)))
914 self.end_headers()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000915 self.wfile.write(response)
Georg Brandl38eceaa2008-05-26 11:14:17 +0000916
Georg Brandl38eceaa2008-05-26 11:14:17 +0000917class DocXMLRPCServer( SimpleXMLRPCServer,
918 XMLRPCDocGenerator):
919 """XML-RPC and HTML documentation server.
920
921 Adds the ability to serve server documentation to the capabilities
922 of SimpleXMLRPCServer.
923 """
924
925 def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler,
Georg Brandlfe991052009-09-16 15:54:04 +0000926 logRequests=True, allow_none=False, encoding=None,
Georg Brandl38eceaa2008-05-26 11:14:17 +0000927 bind_and_activate=True):
928 SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests,
929 allow_none, encoding, bind_and_activate)
930 XMLRPCDocGenerator.__init__(self)
931
932class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler,
933 XMLRPCDocGenerator):
934 """Handler for XML-RPC data and documentation requests passed through
935 CGI"""
936
937 def handle_get(self):
938 """Handles the HTTP GET request.
939
940 Interpret all HTTP GET requests as requests for server
941 documentation.
942 """
943
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000944 response = self.generate_html_documentation().encode('utf-8')
Georg Brandl38eceaa2008-05-26 11:14:17 +0000945
946 print('Content-Type: text/html')
947 print('Content-Length: %d' % len(response))
948 print()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000949 sys.stdout.flush()
950 sys.stdout.buffer.write(response)
951 sys.stdout.buffer.flush()
Georg Brandl38eceaa2008-05-26 11:14:17 +0000952
953 def __init__(self):
954 CGIXMLRPCRequestHandler.__init__(self)
955 XMLRPCDocGenerator.__init__(self)
956
957
Fredrik Lundhb329b712001-09-17 17:35:21 +0000958if __name__ == '__main__':
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000959 print('Running XML-RPC server on port 8000')
Fredrik Lundhb329b712001-09-17 17:35:21 +0000960 server = SimpleXMLRPCServer(("localhost", 8000))
961 server.register_function(pow)
962 server.register_function(lambda x,y: x+y, 'add')
963 server.serve_forever()