blob: 63df97fbce80cdf6fdb57217e627022058def463 [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))
502 self.send_header("X-traceback", traceback.format_exc())
503
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000504 self.send_header("Content-length", "0")
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000505 self.end_headers()
506 else:
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000507 self.send_response(200)
508 self.send_header("Content-type", "text/xml")
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000509 if self.encode_threshold is not None:
510 if len(response) > self.encode_threshold:
511 q = self.accept_encodings().get("gzip", 0)
512 if q:
Kristján Valur Jónssonaefde242009-07-19 22:29:24 +0000513 try:
514 response = gzip_encode(response)
515 self.send_header("Content-Encoding", "gzip")
516 except NotImplementedError:
517 pass
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000518 self.send_header("Content-length", str(len(response)))
519 self.end_headers()
520 self.wfile.write(response)
521
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000522 def decode_request_content(self, data):
523 #support gzip encoding of request
524 encoding = self.headers.get("content-encoding", "identity").lower()
525 if encoding == "identity":
526 return data
527 if encoding == "gzip":
528 try:
529 return gzip_decode(data)
Kristján Valur Jónssonaefde242009-07-19 22:29:24 +0000530 except NotImplementedError:
531 self.send_response(501, "encoding %r not supported" % encoding)
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000532 except ValueError:
533 self.send_response(400, "error decoding gzip content")
534 else:
535 self.send_response(501, "encoding %r not supported" % encoding)
536 self.send_header("Content-length", "0")
537 self.end_headers()
Tim Peters2c60f7a2003-01-29 03:49:43 +0000538
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000539 def report_404 (self):
540 # Report a 404 error
541 self.send_response(404)
Christian Heimes0aa93cd2007-12-08 18:38:20 +0000542 response = b'No such page'
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000543 self.send_header("Content-type", "text/plain")
544 self.send_header("Content-length", str(len(response)))
545 self.end_headers()
546 self.wfile.write(response)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000547
Fredrik Lundhb329b712001-09-17 17:35:21 +0000548 def log_request(self, code='-', size='-'):
549 """Selectively log an accepted request."""
550
551 if self.server.logRequests:
Georg Brandl24420152008-05-26 16:32:26 +0000552 BaseHTTPRequestHandler.log_request(self, code, size)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000553
Alexandre Vassalottice261952008-05-12 02:31:37 +0000554class SimpleXMLRPCServer(socketserver.TCPServer,
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000555 SimpleXMLRPCDispatcher):
Fredrik Lundhb329b712001-09-17 17:35:21 +0000556 """Simple XML-RPC server.
557
558 Simple XML-RPC server that allows functions and a single instance
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000559 to be installed to handle requests. The default implementation
560 attempts to dispatch XML-RPC calls to the functions or instance
561 installed in the server. Override the _dispatch method inhereted
562 from SimpleXMLRPCDispatcher to change this behavior.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000563 """
564
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000565 allow_reuse_address = True
566
Guido van Rossum61e21b52007-08-20 19:06:03 +0000567 # Warning: this is for debugging purposes only! Never set this to True in
568 # production code, as will be sending out sensitive information (exception
569 # and stack trace details) when exceptions are raised inside
570 # SimpleXMLRPCRequestHandler.do_POST
571 _send_traceback_header = False
572
Fredrik Lundhb329b712001-09-17 17:35:21 +0000573 def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
Guido van Rossumd8faa362007-04-27 19:54:29 +0000574 logRequests=True, allow_none=False, encoding=None, bind_and_activate=True):
Fredrik Lundhb329b712001-09-17 17:35:21 +0000575 self.logRequests = logRequests
Tim Peters2c60f7a2003-01-29 03:49:43 +0000576
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000577 SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
Alexandre Vassalottice261952008-05-12 02:31:37 +0000578 socketserver.TCPServer.__init__(self, addr, requestHandler, bind_and_activate)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000579
Tim Peters536cf992005-12-25 23:18:31 +0000580 # [Bug #1222790] If possible, set close-on-exec flag; if a
581 # method spawns a subprocess, the subprocess shouldn't have
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000582 # the listening socket open.
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000583 if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
Andrew M. Kuchling3a976052005-12-04 15:07:41 +0000584 flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
585 flags |= fcntl.FD_CLOEXEC
586 fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
587
Kristján Valur Jónsson1f2a1ae2009-12-16 10:50:44 +0000588class MultiPathXMLRPCServer(SimpleXMLRPCServer):
589 """Multipath XML-RPC Server
590 This specialization of SimpleXMLRPCServer allows the user to create
591 multiple Dispatcher instances and assign them to different
592 HTTP request paths. This makes it possible to run two or more
593 'virtual XML-RPC servers' at the same port.
594 Make sure that the requestHandler accepts the paths in question.
595 """
596 def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
597 logRequests=True, allow_none=False, encoding=None, bind_and_activate=True):
598
599 SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, allow_none,
600 encoding, bind_and_activate)
601 self.dispatchers = {}
602 self.allow_none = allow_none
603 self.encoding = encoding
604
605 def add_dispatcher(self, path, dispatcher):
606 self.dispatchers[path] = dispatcher
607 return dispatcher
608
609 def get_dispatcher(self, path):
610 return self.dispatchers[path]
611
612 def _marshaled_dispatch(self, data, dispatch_method = None, path = None):
613 try:
614 response = self.dispatchers[path]._marshaled_dispatch(
615 data, dispatch_method, path)
616 except:
617 # report low level exception back to server
618 # (each dispatcher should have handled their own
619 # exceptions)
620 exc_type, exc_value = sys.exc_info()[:2]
621 response = xmlrpclib.dumps(
622 xmlrpclib.Fault(1, "%s:%s" % (exc_type, exc_value)),
623 encoding=self.encoding, allow_none=self.allow_none)
624 return response
625
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000626class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher):
627 """Simple handler for XML-RPC data passed through CGI."""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000628
Andrew M. Kuchling427aedb2005-12-04 17:13:12 +0000629 def __init__(self, allow_none=False, encoding=None):
630 SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000631
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000632 def handle_xmlrpc(self, request_text):
633 """Handle a single XML-RPC request"""
Tim Peters2c60f7a2003-01-29 03:49:43 +0000634
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000635 response = self._marshaled_dispatch(request_text)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000636
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000637 print('Content-Type: text/xml')
638 print('Content-Length: %d' % len(response))
639 print()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000640 sys.stdout.flush()
641 sys.stdout.buffer.write(response)
642 sys.stdout.buffer.flush()
Fredrik Lundhb329b712001-09-17 17:35:21 +0000643
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000644 def handle_get(self):
645 """Handle a single HTTP GET request.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000646
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000647 Default implementation indicates an error because
648 XML-RPC uses the POST method.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000649 """
650
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000651 code = 400
Georg Brandl24420152008-05-26 16:32:26 +0000652 message, explain = BaseHTTPRequestHandler.responses[code]
Tim Peters2c60f7a2003-01-29 03:49:43 +0000653
Georg Brandl24420152008-05-26 16:32:26 +0000654 response = http.server.DEFAULT_ERROR_MESSAGE % \
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000655 {
Tim Peters2c60f7a2003-01-29 03:49:43 +0000656 'code' : code,
657 'message' : message,
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000658 'explain' : explain
659 }
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000660 response = response.encode('utf-8')
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000661 print('Status: %d %s' % (code, message))
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000662 print('Content-Type: %s' % http.server.DEFAULT_ERROR_CONTENT_TYPE)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000663 print('Content-Length: %d' % len(response))
664 print()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000665 sys.stdout.flush()
666 sys.stdout.buffer.write(response)
667 sys.stdout.buffer.flush()
Tim Peters2c60f7a2003-01-29 03:49:43 +0000668
Georg Brandlfe991052009-09-16 15:54:04 +0000669 def handle_request(self, request_text=None):
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000670 """Handle a single XML-RPC request passed through a CGI post method.
Tim Peters2c60f7a2003-01-29 03:49:43 +0000671
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000672 If no XML data is given then it is read from stdin. The resulting
673 XML-RPC response is printed to stdout along with the correct HTTP
674 headers.
Fredrik Lundhb329b712001-09-17 17:35:21 +0000675 """
Tim Peters2c60f7a2003-01-29 03:49:43 +0000676
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000677 if request_text is None and \
678 os.environ.get('REQUEST_METHOD', None) == 'GET':
679 self.handle_get()
680 else:
681 # POST data is normally available through stdin
Georg Brandl99412e52009-04-01 04:27:47 +0000682 try:
683 length = int(os.environ.get('CONTENT_LENGTH', None))
Georg Brandlc7485062009-04-01 15:53:15 +0000684 except (ValueError, TypeError):
Georg Brandl99412e52009-04-01 04:27:47 +0000685 length = -1
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000686 if request_text is None:
Georg Brandl99412e52009-04-01 04:27:47 +0000687 request_text = sys.stdin.read(length)
Fredrik Lundhb329b712001-09-17 17:35:21 +0000688
Martin v. Löwisd69663d2003-01-15 11:37:23 +0000689 self.handle_xmlrpc(request_text)
Tim Peters2c60f7a2003-01-29 03:49:43 +0000690
Georg Brandl38eceaa2008-05-26 11:14:17 +0000691
692# -----------------------------------------------------------------------------
693# Self documenting XML-RPC Server.
694
695class ServerHTMLDoc(pydoc.HTMLDoc):
696 """Class used to generate pydoc HTML document for a server"""
697
698 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
699 """Mark up some plain text, given a context of symbols to look for.
700 Each context dictionary maps object names to anchor names."""
701 escape = escape or self.escape
702 results = []
703 here = 0
704
705 # XXX Note that this regular expression does not allow for the
706 # hyperlinking of arbitrary strings being used as method
707 # names. Only methods with names consisting of word characters
708 # and '.'s are hyperlinked.
709 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
710 r'RFC[- ]?(\d+)|'
711 r'PEP[- ]?(\d+)|'
712 r'(self\.)?((?:\w|\.)+))\b')
713 while 1:
714 match = pattern.search(text, here)
715 if not match: break
716 start, end = match.span()
717 results.append(escape(text[here:start]))
718
719 all, scheme, rfc, pep, selfdot, name = match.groups()
720 if scheme:
721 url = escape(all).replace('"', '"')
722 results.append('<a href="%s">%s</a>' % (url, url))
723 elif rfc:
724 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
725 results.append('<a href="%s">%s</a>' % (url, escape(all)))
726 elif pep:
727 url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep)
728 results.append('<a href="%s">%s</a>' % (url, escape(all)))
729 elif text[end:end+1] == '(':
730 results.append(self.namelink(name, methods, funcs, classes))
731 elif selfdot:
732 results.append('self.<strong>%s</strong>' % name)
733 else:
734 results.append(self.namelink(name, classes))
735 here = end
736 results.append(escape(text[here:]))
737 return ''.join(results)
738
739 def docroutine(self, object, name, mod=None,
740 funcs={}, classes={}, methods={}, cl=None):
741 """Produce HTML documentation for a function or method object."""
742
743 anchor = (cl and cl.__name__ or '') + '-' + name
744 note = ''
745
746 title = '<a name="%s"><strong>%s</strong></a>' % (
747 self.escape(anchor), self.escape(name))
748
749 if inspect.ismethod(object):
750 args, varargs, varkw, defaults = inspect.getargspec(object)
751 # exclude the argument bound to the instance, it will be
752 # confusing to the non-Python user
753 argspec = inspect.formatargspec (
754 args[1:],
755 varargs,
756 varkw,
757 defaults,
758 formatvalue=self.formatvalue
759 )
760 elif inspect.isfunction(object):
761 args, varargs, varkw, defaults = inspect.getargspec(object)
762 argspec = inspect.formatargspec(
763 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
764 else:
765 argspec = '(...)'
766
767 if isinstance(object, tuple):
768 argspec = object[0] or argspec
769 docstring = object[1] or ""
770 else:
771 docstring = pydoc.getdoc(object)
772
773 decl = title + argspec + (note and self.grey(
774 '<font face="helvetica, arial">%s</font>' % note))
775
776 doc = self.markup(
777 docstring, self.preformat, funcs, classes, methods)
778 doc = doc and '<dd><tt>%s</tt></dd>' % doc
779 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
780
781 def docserver(self, server_name, package_documentation, methods):
782 """Produce HTML documentation for an XML-RPC server."""
783
784 fdict = {}
785 for key, value in methods.items():
786 fdict[key] = '#-' + key
787 fdict[value] = fdict[key]
788
789 server_name = self.escape(server_name)
790 head = '<big><big><strong>%s</strong></big></big>' % server_name
791 result = self.heading(head, '#ffffff', '#7799ee')
792
793 doc = self.markup(package_documentation, self.preformat, fdict)
794 doc = doc and '<tt>%s</tt>' % doc
795 result = result + '<p>%s</p>\n' % doc
796
797 contents = []
798 method_items = sorted(methods.items())
799 for key, value in method_items:
800 contents.append(self.docroutine(value, key, funcs=fdict))
801 result = result + self.bigsection(
802 'Methods', '#ffffff', '#eeaa77', ''.join(contents))
803
804 return result
805
806class XMLRPCDocGenerator:
807 """Generates documentation for an XML-RPC server.
808
809 This class is designed as mix-in and should not
810 be constructed directly.
811 """
812
813 def __init__(self):
814 # setup variables used for HTML documentation
815 self.server_name = 'XML-RPC Server Documentation'
816 self.server_documentation = \
817 "This server exports the following methods through the XML-RPC "\
818 "protocol."
819 self.server_title = 'XML-RPC Server Documentation'
820
821 def set_server_title(self, server_title):
822 """Set the HTML title of the generated server documentation"""
823
824 self.server_title = server_title
825
826 def set_server_name(self, server_name):
827 """Set the name of the generated HTML server documentation"""
828
829 self.server_name = server_name
830
831 def set_server_documentation(self, server_documentation):
832 """Set the documentation string for the entire server."""
833
834 self.server_documentation = server_documentation
835
836 def generate_html_documentation(self):
837 """generate_html_documentation() => html documentation for the server
838
839 Generates HTML documentation for the server using introspection for
840 installed functions and instances that do not implement the
841 _dispatch method. Alternatively, instances can choose to implement
842 the _get_method_argstring(method_name) method to provide the
843 argument string used in the documentation and the
844 _methodHelp(method_name) method to provide the help text used
845 in the documentation."""
846
847 methods = {}
848
849 for method_name in self.system_listMethods():
850 if method_name in self.funcs:
851 method = self.funcs[method_name]
852 elif self.instance is not None:
853 method_info = [None, None] # argspec, documentation
854 if hasattr(self.instance, '_get_method_argstring'):
855 method_info[0] = self.instance._get_method_argstring(method_name)
856 if hasattr(self.instance, '_methodHelp'):
857 method_info[1] = self.instance._methodHelp(method_name)
858
859 method_info = tuple(method_info)
860 if method_info != (None, None):
861 method = method_info
862 elif not hasattr(self.instance, '_dispatch'):
863 try:
864 method = resolve_dotted_attribute(
865 self.instance,
866 method_name
867 )
868 except AttributeError:
869 method = method_info
870 else:
871 method = method_info
872 else:
873 assert 0, "Could not find method in self.functions and no "\
874 "instance installed"
875
876 methods[method_name] = method
877
878 documenter = ServerHTMLDoc()
879 documentation = documenter.docserver(
880 self.server_name,
881 self.server_documentation,
882 methods
883 )
884
885 return documenter.page(self.server_title, documentation)
886
887class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
888 """XML-RPC and documentation request handler class.
889
890 Handles all HTTP POST requests and attempts to decode them as
891 XML-RPC requests.
892
893 Handles all HTTP GET requests and interprets them as requests
894 for documentation.
895 """
896
897 def do_GET(self):
898 """Handles the HTTP GET request.
899
900 Interpret all HTTP GET requests as requests for server
901 documentation.
902 """
903 # Check that the path is legal
904 if not self.is_rpc_path_valid():
905 self.report_404()
906 return
907
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000908 response = self.server.generate_html_documentation().encode('utf-8')
Georg Brandl38eceaa2008-05-26 11:14:17 +0000909 self.send_response(200)
910 self.send_header("Content-type", "text/html")
911 self.send_header("Content-length", str(len(response)))
912 self.end_headers()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000913 self.wfile.write(response)
Georg Brandl38eceaa2008-05-26 11:14:17 +0000914
Georg Brandl38eceaa2008-05-26 11:14:17 +0000915class DocXMLRPCServer( SimpleXMLRPCServer,
916 XMLRPCDocGenerator):
917 """XML-RPC and HTML documentation server.
918
919 Adds the ability to serve server documentation to the capabilities
920 of SimpleXMLRPCServer.
921 """
922
923 def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler,
Georg Brandlfe991052009-09-16 15:54:04 +0000924 logRequests=True, allow_none=False, encoding=None,
Georg Brandl38eceaa2008-05-26 11:14:17 +0000925 bind_and_activate=True):
926 SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests,
927 allow_none, encoding, bind_and_activate)
928 XMLRPCDocGenerator.__init__(self)
929
930class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler,
931 XMLRPCDocGenerator):
932 """Handler for XML-RPC data and documentation requests passed through
933 CGI"""
934
935 def handle_get(self):
936 """Handles the HTTP GET request.
937
938 Interpret all HTTP GET requests as requests for server
939 documentation.
940 """
941
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000942 response = self.generate_html_documentation().encode('utf-8')
Georg Brandl38eceaa2008-05-26 11:14:17 +0000943
944 print('Content-Type: text/html')
945 print('Content-Length: %d' % len(response))
946 print()
Senthil Kumaranb3af08f2009-04-01 20:20:43 +0000947 sys.stdout.flush()
948 sys.stdout.buffer.write(response)
949 sys.stdout.buffer.flush()
Georg Brandl38eceaa2008-05-26 11:14:17 +0000950
951 def __init__(self):
952 CGIXMLRPCRequestHandler.__init__(self)
953 XMLRPCDocGenerator.__init__(self)
954
955
Fredrik Lundhb329b712001-09-17 17:35:21 +0000956if __name__ == '__main__':
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000957 print('Running XML-RPC server on port 8000')
Fredrik Lundhb329b712001-09-17 17:35:21 +0000958 server = SimpleXMLRPCServer(("localhost", 8000))
959 server.register_function(pow)
960 server.register_function(lambda x,y: x+y, 'add')
961 server.serve_forever()