blob: eb231d3db497265374cb1dad5086a47ae58a561d [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:
70 raise '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
Guido van Rossumd59da4b2007-05-22 18:11:13 +0000152 hasattr(getattr(obj, member), '__call__')]
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
243 existing method through subclassing is the prefered means
244 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
605 self.encoding = encoding
606
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]
623 response = xmlrpclib.dumps(
624 xmlrpclib.Fault(1, "%s:%s" % (exc_type, exc_value)),
625 encoding=self.encoding, allow_none=self.allow_none)
626 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()