1*cda5da8dSAndroid Build Coastguard Workerr"""XML-RPC Servers. 2*cda5da8dSAndroid Build Coastguard Worker 3*cda5da8dSAndroid Build Coastguard WorkerThis module can be used to create simple XML-RPC servers 4*cda5da8dSAndroid Build Coastguard Workerby creating a server and either installing functions, a 5*cda5da8dSAndroid Build Coastguard Workerclass instance, or by extending the SimpleXMLRPCServer 6*cda5da8dSAndroid Build Coastguard Workerclass. 7*cda5da8dSAndroid Build Coastguard Worker 8*cda5da8dSAndroid Build Coastguard WorkerIt can also be used to handle XML-RPC requests in a CGI 9*cda5da8dSAndroid Build Coastguard Workerenvironment using CGIXMLRPCRequestHandler. 10*cda5da8dSAndroid Build Coastguard Worker 11*cda5da8dSAndroid Build Coastguard WorkerThe Doc* classes can be used to create XML-RPC servers that 12*cda5da8dSAndroid Build Coastguard Workerserve pydoc-style documentation in response to HTTP 13*cda5da8dSAndroid Build Coastguard WorkerGET requests. This documentation is dynamically generated 14*cda5da8dSAndroid Build Coastguard Workerbased on the functions and methods registered with the 15*cda5da8dSAndroid Build Coastguard Workerserver. 16*cda5da8dSAndroid Build Coastguard Worker 17*cda5da8dSAndroid Build Coastguard WorkerA list of possible usage patterns follows: 18*cda5da8dSAndroid Build Coastguard Worker 19*cda5da8dSAndroid Build Coastguard Worker1. Install functions: 20*cda5da8dSAndroid Build Coastguard Worker 21*cda5da8dSAndroid Build Coastguard Workerserver = SimpleXMLRPCServer(("localhost", 8000)) 22*cda5da8dSAndroid Build Coastguard Workerserver.register_function(pow) 23*cda5da8dSAndroid Build Coastguard Workerserver.register_function(lambda x,y: x+y, 'add') 24*cda5da8dSAndroid Build Coastguard Workerserver.serve_forever() 25*cda5da8dSAndroid Build Coastguard Worker 26*cda5da8dSAndroid Build Coastguard Worker2. Install an instance: 27*cda5da8dSAndroid Build Coastguard Worker 28*cda5da8dSAndroid Build Coastguard Workerclass MyFuncs: 29*cda5da8dSAndroid Build Coastguard Worker def __init__(self): 30*cda5da8dSAndroid Build Coastguard Worker # make all of the sys functions available through sys.func_name 31*cda5da8dSAndroid Build Coastguard Worker import sys 32*cda5da8dSAndroid Build Coastguard Worker self.sys = sys 33*cda5da8dSAndroid Build Coastguard Worker def _listMethods(self): 34*cda5da8dSAndroid Build Coastguard Worker # implement this method so that system.listMethods 35*cda5da8dSAndroid Build Coastguard Worker # knows to advertise the sys methods 36*cda5da8dSAndroid Build Coastguard Worker return list_public_methods(self) + \ 37*cda5da8dSAndroid Build Coastguard Worker ['sys.' + method for method in list_public_methods(self.sys)] 38*cda5da8dSAndroid Build Coastguard Worker def pow(self, x, y): return pow(x, y) 39*cda5da8dSAndroid Build Coastguard Worker def add(self, x, y) : return x + y 40*cda5da8dSAndroid Build Coastguard Worker 41*cda5da8dSAndroid Build Coastguard Workerserver = SimpleXMLRPCServer(("localhost", 8000)) 42*cda5da8dSAndroid Build Coastguard Workerserver.register_introspection_functions() 43*cda5da8dSAndroid Build Coastguard Workerserver.register_instance(MyFuncs()) 44*cda5da8dSAndroid Build Coastguard Workerserver.serve_forever() 45*cda5da8dSAndroid Build Coastguard Worker 46*cda5da8dSAndroid Build Coastguard Worker3. Install an instance with custom dispatch method: 47*cda5da8dSAndroid Build Coastguard Worker 48*cda5da8dSAndroid Build Coastguard Workerclass Math: 49*cda5da8dSAndroid Build Coastguard Worker def _listMethods(self): 50*cda5da8dSAndroid Build Coastguard Worker # this method must be present for system.listMethods 51*cda5da8dSAndroid Build Coastguard Worker # to work 52*cda5da8dSAndroid Build Coastguard Worker return ['add', 'pow'] 53*cda5da8dSAndroid Build Coastguard Worker def _methodHelp(self, method): 54*cda5da8dSAndroid Build Coastguard Worker # this method must be present for system.methodHelp 55*cda5da8dSAndroid Build Coastguard Worker # to work 56*cda5da8dSAndroid Build Coastguard Worker if method == 'add': 57*cda5da8dSAndroid Build Coastguard Worker return "add(2,3) => 5" 58*cda5da8dSAndroid Build Coastguard Worker elif method == 'pow': 59*cda5da8dSAndroid Build Coastguard Worker return "pow(x, y[, z]) => number" 60*cda5da8dSAndroid Build Coastguard Worker else: 61*cda5da8dSAndroid Build Coastguard Worker # By convention, return empty 62*cda5da8dSAndroid Build Coastguard Worker # string if no help is available 63*cda5da8dSAndroid Build Coastguard Worker return "" 64*cda5da8dSAndroid Build Coastguard Worker def _dispatch(self, method, params): 65*cda5da8dSAndroid Build Coastguard Worker if method == 'pow': 66*cda5da8dSAndroid Build Coastguard Worker return pow(*params) 67*cda5da8dSAndroid Build Coastguard Worker elif method == 'add': 68*cda5da8dSAndroid Build Coastguard Worker return params[0] + params[1] 69*cda5da8dSAndroid Build Coastguard Worker else: 70*cda5da8dSAndroid Build Coastguard Worker raise ValueError('bad method') 71*cda5da8dSAndroid Build Coastguard Worker 72*cda5da8dSAndroid Build Coastguard Workerserver = SimpleXMLRPCServer(("localhost", 8000)) 73*cda5da8dSAndroid Build Coastguard Workerserver.register_introspection_functions() 74*cda5da8dSAndroid Build Coastguard Workerserver.register_instance(Math()) 75*cda5da8dSAndroid Build Coastguard Workerserver.serve_forever() 76*cda5da8dSAndroid Build Coastguard Worker 77*cda5da8dSAndroid Build Coastguard Worker4. Subclass SimpleXMLRPCServer: 78*cda5da8dSAndroid Build Coastguard Worker 79*cda5da8dSAndroid Build Coastguard Workerclass MathServer(SimpleXMLRPCServer): 80*cda5da8dSAndroid Build Coastguard Worker def _dispatch(self, method, params): 81*cda5da8dSAndroid Build Coastguard Worker try: 82*cda5da8dSAndroid Build Coastguard Worker # We are forcing the 'export_' prefix on methods that are 83*cda5da8dSAndroid Build Coastguard Worker # callable through XML-RPC to prevent potential security 84*cda5da8dSAndroid Build Coastguard Worker # problems 85*cda5da8dSAndroid Build Coastguard Worker func = getattr(self, 'export_' + method) 86*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 87*cda5da8dSAndroid Build Coastguard Worker raise Exception('method "%s" is not supported' % method) 88*cda5da8dSAndroid Build Coastguard Worker else: 89*cda5da8dSAndroid Build Coastguard Worker return func(*params) 90*cda5da8dSAndroid Build Coastguard Worker 91*cda5da8dSAndroid Build Coastguard Worker def export_add(self, x, y): 92*cda5da8dSAndroid Build Coastguard Worker return x + y 93*cda5da8dSAndroid Build Coastguard Worker 94*cda5da8dSAndroid Build Coastguard Workerserver = MathServer(("localhost", 8000)) 95*cda5da8dSAndroid Build Coastguard Workerserver.serve_forever() 96*cda5da8dSAndroid Build Coastguard Worker 97*cda5da8dSAndroid Build Coastguard Worker5. CGI script: 98*cda5da8dSAndroid Build Coastguard Worker 99*cda5da8dSAndroid Build Coastguard Workerserver = CGIXMLRPCRequestHandler() 100*cda5da8dSAndroid Build Coastguard Workerserver.register_function(pow) 101*cda5da8dSAndroid Build Coastguard Workerserver.handle_request() 102*cda5da8dSAndroid Build Coastguard Worker""" 103*cda5da8dSAndroid Build Coastguard Worker 104*cda5da8dSAndroid Build Coastguard Worker# Written by Brian Quinlan ([email protected]). 105*cda5da8dSAndroid Build Coastguard Worker# Based on code written by Fredrik Lundh. 106*cda5da8dSAndroid Build Coastguard Worker 107*cda5da8dSAndroid Build Coastguard Workerfrom xmlrpc.client import Fault, dumps, loads, gzip_encode, gzip_decode 108*cda5da8dSAndroid Build Coastguard Workerfrom http.server import BaseHTTPRequestHandler 109*cda5da8dSAndroid Build Coastguard Workerfrom functools import partial 110*cda5da8dSAndroid Build Coastguard Workerfrom inspect import signature 111*cda5da8dSAndroid Build Coastguard Workerimport html 112*cda5da8dSAndroid Build Coastguard Workerimport http.server 113*cda5da8dSAndroid Build Coastguard Workerimport socketserver 114*cda5da8dSAndroid Build Coastguard Workerimport sys 115*cda5da8dSAndroid Build Coastguard Workerimport os 116*cda5da8dSAndroid Build Coastguard Workerimport re 117*cda5da8dSAndroid Build Coastguard Workerimport pydoc 118*cda5da8dSAndroid Build Coastguard Workerimport traceback 119*cda5da8dSAndroid Build Coastguard Workertry: 120*cda5da8dSAndroid Build Coastguard Worker import fcntl 121*cda5da8dSAndroid Build Coastguard Workerexcept ImportError: 122*cda5da8dSAndroid Build Coastguard Worker fcntl = None 123*cda5da8dSAndroid Build Coastguard Worker 124*cda5da8dSAndroid Build Coastguard Workerdef resolve_dotted_attribute(obj, attr, allow_dotted_names=True): 125*cda5da8dSAndroid Build Coastguard Worker """resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d 126*cda5da8dSAndroid Build Coastguard Worker 127*cda5da8dSAndroid Build Coastguard Worker Resolves a dotted attribute name to an object. Raises 128*cda5da8dSAndroid Build Coastguard Worker an AttributeError if any attribute in the chain starts with a '_'. 129*cda5da8dSAndroid Build Coastguard Worker 130*cda5da8dSAndroid Build Coastguard Worker If the optional allow_dotted_names argument is false, dots are not 131*cda5da8dSAndroid Build Coastguard Worker supported and this function operates similar to getattr(obj, attr). 132*cda5da8dSAndroid Build Coastguard Worker """ 133*cda5da8dSAndroid Build Coastguard Worker 134*cda5da8dSAndroid Build Coastguard Worker if allow_dotted_names: 135*cda5da8dSAndroid Build Coastguard Worker attrs = attr.split('.') 136*cda5da8dSAndroid Build Coastguard Worker else: 137*cda5da8dSAndroid Build Coastguard Worker attrs = [attr] 138*cda5da8dSAndroid Build Coastguard Worker 139*cda5da8dSAndroid Build Coastguard Worker for i in attrs: 140*cda5da8dSAndroid Build Coastguard Worker if i.startswith('_'): 141*cda5da8dSAndroid Build Coastguard Worker raise AttributeError( 142*cda5da8dSAndroid Build Coastguard Worker 'attempt to access private attribute "%s"' % i 143*cda5da8dSAndroid Build Coastguard Worker ) 144*cda5da8dSAndroid Build Coastguard Worker else: 145*cda5da8dSAndroid Build Coastguard Worker obj = getattr(obj,i) 146*cda5da8dSAndroid Build Coastguard Worker return obj 147*cda5da8dSAndroid Build Coastguard Worker 148*cda5da8dSAndroid Build Coastguard Workerdef list_public_methods(obj): 149*cda5da8dSAndroid Build Coastguard Worker """Returns a list of attribute strings, found in the specified 150*cda5da8dSAndroid Build Coastguard Worker object, which represent callable attributes""" 151*cda5da8dSAndroid Build Coastguard Worker 152*cda5da8dSAndroid Build Coastguard Worker return [member for member in dir(obj) 153*cda5da8dSAndroid Build Coastguard Worker if not member.startswith('_') and 154*cda5da8dSAndroid Build Coastguard Worker callable(getattr(obj, member))] 155*cda5da8dSAndroid Build Coastguard Worker 156*cda5da8dSAndroid Build Coastguard Workerclass SimpleXMLRPCDispatcher: 157*cda5da8dSAndroid Build Coastguard Worker """Mix-in class that dispatches XML-RPC requests. 158*cda5da8dSAndroid Build Coastguard Worker 159*cda5da8dSAndroid Build Coastguard Worker This class is used to register XML-RPC method handlers 160*cda5da8dSAndroid Build Coastguard Worker and then to dispatch them. This class doesn't need to be 161*cda5da8dSAndroid Build Coastguard Worker instanced directly when used by SimpleXMLRPCServer but it 162*cda5da8dSAndroid Build Coastguard Worker can be instanced when used by the MultiPathXMLRPCServer 163*cda5da8dSAndroid Build Coastguard Worker """ 164*cda5da8dSAndroid Build Coastguard Worker 165*cda5da8dSAndroid Build Coastguard Worker def __init__(self, allow_none=False, encoding=None, 166*cda5da8dSAndroid Build Coastguard Worker use_builtin_types=False): 167*cda5da8dSAndroid Build Coastguard Worker self.funcs = {} 168*cda5da8dSAndroid Build Coastguard Worker self.instance = None 169*cda5da8dSAndroid Build Coastguard Worker self.allow_none = allow_none 170*cda5da8dSAndroid Build Coastguard Worker self.encoding = encoding or 'utf-8' 171*cda5da8dSAndroid Build Coastguard Worker self.use_builtin_types = use_builtin_types 172*cda5da8dSAndroid Build Coastguard Worker 173*cda5da8dSAndroid Build Coastguard Worker def register_instance(self, instance, allow_dotted_names=False): 174*cda5da8dSAndroid Build Coastguard Worker """Registers an instance to respond to XML-RPC requests. 175*cda5da8dSAndroid Build Coastguard Worker 176*cda5da8dSAndroid Build Coastguard Worker Only one instance can be installed at a time. 177*cda5da8dSAndroid Build Coastguard Worker 178*cda5da8dSAndroid Build Coastguard Worker If the registered instance has a _dispatch method then that 179*cda5da8dSAndroid Build Coastguard Worker method will be called with the name of the XML-RPC method and 180*cda5da8dSAndroid Build Coastguard Worker its parameters as a tuple 181*cda5da8dSAndroid Build Coastguard Worker e.g. instance._dispatch('add',(2,3)) 182*cda5da8dSAndroid Build Coastguard Worker 183*cda5da8dSAndroid Build Coastguard Worker If the registered instance does not have a _dispatch method 184*cda5da8dSAndroid Build Coastguard Worker then the instance will be searched to find a matching method 185*cda5da8dSAndroid Build Coastguard Worker and, if found, will be called. Methods beginning with an '_' 186*cda5da8dSAndroid Build Coastguard Worker are considered private and will not be called by 187*cda5da8dSAndroid Build Coastguard Worker SimpleXMLRPCServer. 188*cda5da8dSAndroid Build Coastguard Worker 189*cda5da8dSAndroid Build Coastguard Worker If a registered function matches an XML-RPC request, then it 190*cda5da8dSAndroid Build Coastguard Worker will be called instead of the registered instance. 191*cda5da8dSAndroid Build Coastguard Worker 192*cda5da8dSAndroid Build Coastguard Worker If the optional allow_dotted_names argument is true and the 193*cda5da8dSAndroid Build Coastguard Worker instance does not have a _dispatch method, method names 194*cda5da8dSAndroid Build Coastguard Worker containing dots are supported and resolved, as long as none of 195*cda5da8dSAndroid Build Coastguard Worker the name segments start with an '_'. 196*cda5da8dSAndroid Build Coastguard Worker 197*cda5da8dSAndroid Build Coastguard Worker *** SECURITY WARNING: *** 198*cda5da8dSAndroid Build Coastguard Worker 199*cda5da8dSAndroid Build Coastguard Worker Enabling the allow_dotted_names options allows intruders 200*cda5da8dSAndroid Build Coastguard Worker to access your module's global variables and may allow 201*cda5da8dSAndroid Build Coastguard Worker intruders to execute arbitrary code on your machine. Only 202*cda5da8dSAndroid Build Coastguard Worker use this option on a secure, closed network. 203*cda5da8dSAndroid Build Coastguard Worker 204*cda5da8dSAndroid Build Coastguard Worker """ 205*cda5da8dSAndroid Build Coastguard Worker 206*cda5da8dSAndroid Build Coastguard Worker self.instance = instance 207*cda5da8dSAndroid Build Coastguard Worker self.allow_dotted_names = allow_dotted_names 208*cda5da8dSAndroid Build Coastguard Worker 209*cda5da8dSAndroid Build Coastguard Worker def register_function(self, function=None, name=None): 210*cda5da8dSAndroid Build Coastguard Worker """Registers a function to respond to XML-RPC requests. 211*cda5da8dSAndroid Build Coastguard Worker 212*cda5da8dSAndroid Build Coastguard Worker The optional name argument can be used to set a Unicode name 213*cda5da8dSAndroid Build Coastguard Worker for the function. 214*cda5da8dSAndroid Build Coastguard Worker """ 215*cda5da8dSAndroid Build Coastguard Worker # decorator factory 216*cda5da8dSAndroid Build Coastguard Worker if function is None: 217*cda5da8dSAndroid Build Coastguard Worker return partial(self.register_function, name=name) 218*cda5da8dSAndroid Build Coastguard Worker 219*cda5da8dSAndroid Build Coastguard Worker if name is None: 220*cda5da8dSAndroid Build Coastguard Worker name = function.__name__ 221*cda5da8dSAndroid Build Coastguard Worker self.funcs[name] = function 222*cda5da8dSAndroid Build Coastguard Worker 223*cda5da8dSAndroid Build Coastguard Worker return function 224*cda5da8dSAndroid Build Coastguard Worker 225*cda5da8dSAndroid Build Coastguard Worker def register_introspection_functions(self): 226*cda5da8dSAndroid Build Coastguard Worker """Registers the XML-RPC introspection methods in the system 227*cda5da8dSAndroid Build Coastguard Worker namespace. 228*cda5da8dSAndroid Build Coastguard Worker 229*cda5da8dSAndroid Build Coastguard Worker see http://xmlrpc.usefulinc.com/doc/reserved.html 230*cda5da8dSAndroid Build Coastguard Worker """ 231*cda5da8dSAndroid Build Coastguard Worker 232*cda5da8dSAndroid Build Coastguard Worker self.funcs.update({'system.listMethods' : self.system_listMethods, 233*cda5da8dSAndroid Build Coastguard Worker 'system.methodSignature' : self.system_methodSignature, 234*cda5da8dSAndroid Build Coastguard Worker 'system.methodHelp' : self.system_methodHelp}) 235*cda5da8dSAndroid Build Coastguard Worker 236*cda5da8dSAndroid Build Coastguard Worker def register_multicall_functions(self): 237*cda5da8dSAndroid Build Coastguard Worker """Registers the XML-RPC multicall method in the system 238*cda5da8dSAndroid Build Coastguard Worker namespace. 239*cda5da8dSAndroid Build Coastguard Worker 240*cda5da8dSAndroid Build Coastguard Worker see http://www.xmlrpc.com/discuss/msgReader$1208""" 241*cda5da8dSAndroid Build Coastguard Worker 242*cda5da8dSAndroid Build Coastguard Worker self.funcs.update({'system.multicall' : self.system_multicall}) 243*cda5da8dSAndroid Build Coastguard Worker 244*cda5da8dSAndroid Build Coastguard Worker def _marshaled_dispatch(self, data, dispatch_method = None, path = None): 245*cda5da8dSAndroid Build Coastguard Worker """Dispatches an XML-RPC method from marshalled (XML) data. 246*cda5da8dSAndroid Build Coastguard Worker 247*cda5da8dSAndroid Build Coastguard Worker XML-RPC methods are dispatched from the marshalled (XML) data 248*cda5da8dSAndroid Build Coastguard Worker using the _dispatch method and the result is returned as 249*cda5da8dSAndroid Build Coastguard Worker marshalled data. For backwards compatibility, a dispatch 250*cda5da8dSAndroid Build Coastguard Worker function can be provided as an argument (see comment in 251*cda5da8dSAndroid Build Coastguard Worker SimpleXMLRPCRequestHandler.do_POST) but overriding the 252*cda5da8dSAndroid Build Coastguard Worker existing method through subclassing is the preferred means 253*cda5da8dSAndroid Build Coastguard Worker of changing method dispatch behavior. 254*cda5da8dSAndroid Build Coastguard Worker """ 255*cda5da8dSAndroid Build Coastguard Worker 256*cda5da8dSAndroid Build Coastguard Worker try: 257*cda5da8dSAndroid Build Coastguard Worker params, method = loads(data, use_builtin_types=self.use_builtin_types) 258*cda5da8dSAndroid Build Coastguard Worker 259*cda5da8dSAndroid Build Coastguard Worker # generate response 260*cda5da8dSAndroid Build Coastguard Worker if dispatch_method is not None: 261*cda5da8dSAndroid Build Coastguard Worker response = dispatch_method(method, params) 262*cda5da8dSAndroid Build Coastguard Worker else: 263*cda5da8dSAndroid Build Coastguard Worker response = self._dispatch(method, params) 264*cda5da8dSAndroid Build Coastguard Worker # wrap response in a singleton tuple 265*cda5da8dSAndroid Build Coastguard Worker response = (response,) 266*cda5da8dSAndroid Build Coastguard Worker response = dumps(response, methodresponse=1, 267*cda5da8dSAndroid Build Coastguard Worker allow_none=self.allow_none, encoding=self.encoding) 268*cda5da8dSAndroid Build Coastguard Worker except Fault as fault: 269*cda5da8dSAndroid Build Coastguard Worker response = dumps(fault, allow_none=self.allow_none, 270*cda5da8dSAndroid Build Coastguard Worker encoding=self.encoding) 271*cda5da8dSAndroid Build Coastguard Worker except BaseException as exc: 272*cda5da8dSAndroid Build Coastguard Worker response = dumps( 273*cda5da8dSAndroid Build Coastguard Worker Fault(1, "%s:%s" % (type(exc), exc)), 274*cda5da8dSAndroid Build Coastguard Worker encoding=self.encoding, allow_none=self.allow_none, 275*cda5da8dSAndroid Build Coastguard Worker ) 276*cda5da8dSAndroid Build Coastguard Worker 277*cda5da8dSAndroid Build Coastguard Worker return response.encode(self.encoding, 'xmlcharrefreplace') 278*cda5da8dSAndroid Build Coastguard Worker 279*cda5da8dSAndroid Build Coastguard Worker def system_listMethods(self): 280*cda5da8dSAndroid Build Coastguard Worker """system.listMethods() => ['add', 'subtract', 'multiple'] 281*cda5da8dSAndroid Build Coastguard Worker 282*cda5da8dSAndroid Build Coastguard Worker Returns a list of the methods supported by the server.""" 283*cda5da8dSAndroid Build Coastguard Worker 284*cda5da8dSAndroid Build Coastguard Worker methods = set(self.funcs.keys()) 285*cda5da8dSAndroid Build Coastguard Worker if self.instance is not None: 286*cda5da8dSAndroid Build Coastguard Worker # Instance can implement _listMethod to return a list of 287*cda5da8dSAndroid Build Coastguard Worker # methods 288*cda5da8dSAndroid Build Coastguard Worker if hasattr(self.instance, '_listMethods'): 289*cda5da8dSAndroid Build Coastguard Worker methods |= set(self.instance._listMethods()) 290*cda5da8dSAndroid Build Coastguard Worker # if the instance has a _dispatch method then we 291*cda5da8dSAndroid Build Coastguard Worker # don't have enough information to provide a list 292*cda5da8dSAndroid Build Coastguard Worker # of methods 293*cda5da8dSAndroid Build Coastguard Worker elif not hasattr(self.instance, '_dispatch'): 294*cda5da8dSAndroid Build Coastguard Worker methods |= set(list_public_methods(self.instance)) 295*cda5da8dSAndroid Build Coastguard Worker return sorted(methods) 296*cda5da8dSAndroid Build Coastguard Worker 297*cda5da8dSAndroid Build Coastguard Worker def system_methodSignature(self, method_name): 298*cda5da8dSAndroid Build Coastguard Worker """system.methodSignature('add') => [double, int, int] 299*cda5da8dSAndroid Build Coastguard Worker 300*cda5da8dSAndroid Build Coastguard Worker Returns a list describing the signature of the method. In the 301*cda5da8dSAndroid Build Coastguard Worker above example, the add method takes two integers as arguments 302*cda5da8dSAndroid Build Coastguard Worker and returns a double result. 303*cda5da8dSAndroid Build Coastguard Worker 304*cda5da8dSAndroid Build Coastguard Worker This server does NOT support system.methodSignature.""" 305*cda5da8dSAndroid Build Coastguard Worker 306*cda5da8dSAndroid Build Coastguard Worker # See http://xmlrpc.usefulinc.com/doc/sysmethodsig.html 307*cda5da8dSAndroid Build Coastguard Worker 308*cda5da8dSAndroid Build Coastguard Worker return 'signatures not supported' 309*cda5da8dSAndroid Build Coastguard Worker 310*cda5da8dSAndroid Build Coastguard Worker def system_methodHelp(self, method_name): 311*cda5da8dSAndroid Build Coastguard Worker """system.methodHelp('add') => "Adds two integers together" 312*cda5da8dSAndroid Build Coastguard Worker 313*cda5da8dSAndroid Build Coastguard Worker Returns a string containing documentation for the specified method.""" 314*cda5da8dSAndroid Build Coastguard Worker 315*cda5da8dSAndroid Build Coastguard Worker method = None 316*cda5da8dSAndroid Build Coastguard Worker if method_name in self.funcs: 317*cda5da8dSAndroid Build Coastguard Worker method = self.funcs[method_name] 318*cda5da8dSAndroid Build Coastguard Worker elif self.instance is not None: 319*cda5da8dSAndroid Build Coastguard Worker # Instance can implement _methodHelp to return help for a method 320*cda5da8dSAndroid Build Coastguard Worker if hasattr(self.instance, '_methodHelp'): 321*cda5da8dSAndroid Build Coastguard Worker return self.instance._methodHelp(method_name) 322*cda5da8dSAndroid Build Coastguard Worker # if the instance has a _dispatch method then we 323*cda5da8dSAndroid Build Coastguard Worker # don't have enough information to provide help 324*cda5da8dSAndroid Build Coastguard Worker elif not hasattr(self.instance, '_dispatch'): 325*cda5da8dSAndroid Build Coastguard Worker try: 326*cda5da8dSAndroid Build Coastguard Worker method = resolve_dotted_attribute( 327*cda5da8dSAndroid Build Coastguard Worker self.instance, 328*cda5da8dSAndroid Build Coastguard Worker method_name, 329*cda5da8dSAndroid Build Coastguard Worker self.allow_dotted_names 330*cda5da8dSAndroid Build Coastguard Worker ) 331*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 332*cda5da8dSAndroid Build Coastguard Worker pass 333*cda5da8dSAndroid Build Coastguard Worker 334*cda5da8dSAndroid Build Coastguard Worker # Note that we aren't checking that the method actually 335*cda5da8dSAndroid Build Coastguard Worker # be a callable object of some kind 336*cda5da8dSAndroid Build Coastguard Worker if method is None: 337*cda5da8dSAndroid Build Coastguard Worker return "" 338*cda5da8dSAndroid Build Coastguard Worker else: 339*cda5da8dSAndroid Build Coastguard Worker return pydoc.getdoc(method) 340*cda5da8dSAndroid Build Coastguard Worker 341*cda5da8dSAndroid Build Coastguard Worker def system_multicall(self, call_list): 342*cda5da8dSAndroid Build Coastguard Worker """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \ 343*cda5da8dSAndroid Build Coastguard Worker[[4], ...] 344*cda5da8dSAndroid Build Coastguard Worker 345*cda5da8dSAndroid Build Coastguard Worker Allows the caller to package multiple XML-RPC calls into a single 346*cda5da8dSAndroid Build Coastguard Worker request. 347*cda5da8dSAndroid Build Coastguard Worker 348*cda5da8dSAndroid Build Coastguard Worker See http://www.xmlrpc.com/discuss/msgReader$1208 349*cda5da8dSAndroid Build Coastguard Worker """ 350*cda5da8dSAndroid Build Coastguard Worker 351*cda5da8dSAndroid Build Coastguard Worker results = [] 352*cda5da8dSAndroid Build Coastguard Worker for call in call_list: 353*cda5da8dSAndroid Build Coastguard Worker method_name = call['methodName'] 354*cda5da8dSAndroid Build Coastguard Worker params = call['params'] 355*cda5da8dSAndroid Build Coastguard Worker 356*cda5da8dSAndroid Build Coastguard Worker try: 357*cda5da8dSAndroid Build Coastguard Worker # XXX A marshalling error in any response will fail the entire 358*cda5da8dSAndroid Build Coastguard Worker # multicall. If someone cares they should fix this. 359*cda5da8dSAndroid Build Coastguard Worker results.append([self._dispatch(method_name, params)]) 360*cda5da8dSAndroid Build Coastguard Worker except Fault as fault: 361*cda5da8dSAndroid Build Coastguard Worker results.append( 362*cda5da8dSAndroid Build Coastguard Worker {'faultCode' : fault.faultCode, 363*cda5da8dSAndroid Build Coastguard Worker 'faultString' : fault.faultString} 364*cda5da8dSAndroid Build Coastguard Worker ) 365*cda5da8dSAndroid Build Coastguard Worker except BaseException as exc: 366*cda5da8dSAndroid Build Coastguard Worker results.append( 367*cda5da8dSAndroid Build Coastguard Worker {'faultCode' : 1, 368*cda5da8dSAndroid Build Coastguard Worker 'faultString' : "%s:%s" % (type(exc), exc)} 369*cda5da8dSAndroid Build Coastguard Worker ) 370*cda5da8dSAndroid Build Coastguard Worker return results 371*cda5da8dSAndroid Build Coastguard Worker 372*cda5da8dSAndroid Build Coastguard Worker def _dispatch(self, method, params): 373*cda5da8dSAndroid Build Coastguard Worker """Dispatches the XML-RPC method. 374*cda5da8dSAndroid Build Coastguard Worker 375*cda5da8dSAndroid Build Coastguard Worker XML-RPC calls are forwarded to a registered function that 376*cda5da8dSAndroid Build Coastguard Worker matches the called XML-RPC method name. If no such function 377*cda5da8dSAndroid Build Coastguard Worker exists then the call is forwarded to the registered instance, 378*cda5da8dSAndroid Build Coastguard Worker if available. 379*cda5da8dSAndroid Build Coastguard Worker 380*cda5da8dSAndroid Build Coastguard Worker If the registered instance has a _dispatch method then that 381*cda5da8dSAndroid Build Coastguard Worker method will be called with the name of the XML-RPC method and 382*cda5da8dSAndroid Build Coastguard Worker its parameters as a tuple 383*cda5da8dSAndroid Build Coastguard Worker e.g. instance._dispatch('add',(2,3)) 384*cda5da8dSAndroid Build Coastguard Worker 385*cda5da8dSAndroid Build Coastguard Worker If the registered instance does not have a _dispatch method 386*cda5da8dSAndroid Build Coastguard Worker then the instance will be searched to find a matching method 387*cda5da8dSAndroid Build Coastguard Worker and, if found, will be called. 388*cda5da8dSAndroid Build Coastguard Worker 389*cda5da8dSAndroid Build Coastguard Worker Methods beginning with an '_' are considered private and will 390*cda5da8dSAndroid Build Coastguard Worker not be called. 391*cda5da8dSAndroid Build Coastguard Worker """ 392*cda5da8dSAndroid Build Coastguard Worker 393*cda5da8dSAndroid Build Coastguard Worker try: 394*cda5da8dSAndroid Build Coastguard Worker # call the matching registered function 395*cda5da8dSAndroid Build Coastguard Worker func = self.funcs[method] 396*cda5da8dSAndroid Build Coastguard Worker except KeyError: 397*cda5da8dSAndroid Build Coastguard Worker pass 398*cda5da8dSAndroid Build Coastguard Worker else: 399*cda5da8dSAndroid Build Coastguard Worker if func is not None: 400*cda5da8dSAndroid Build Coastguard Worker return func(*params) 401*cda5da8dSAndroid Build Coastguard Worker raise Exception('method "%s" is not supported' % method) 402*cda5da8dSAndroid Build Coastguard Worker 403*cda5da8dSAndroid Build Coastguard Worker if self.instance is not None: 404*cda5da8dSAndroid Build Coastguard Worker if hasattr(self.instance, '_dispatch'): 405*cda5da8dSAndroid Build Coastguard Worker # call the `_dispatch` method on the instance 406*cda5da8dSAndroid Build Coastguard Worker return self.instance._dispatch(method, params) 407*cda5da8dSAndroid Build Coastguard Worker 408*cda5da8dSAndroid Build Coastguard Worker # call the instance's method directly 409*cda5da8dSAndroid Build Coastguard Worker try: 410*cda5da8dSAndroid Build Coastguard Worker func = resolve_dotted_attribute( 411*cda5da8dSAndroid Build Coastguard Worker self.instance, 412*cda5da8dSAndroid Build Coastguard Worker method, 413*cda5da8dSAndroid Build Coastguard Worker self.allow_dotted_names 414*cda5da8dSAndroid Build Coastguard Worker ) 415*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 416*cda5da8dSAndroid Build Coastguard Worker pass 417*cda5da8dSAndroid Build Coastguard Worker else: 418*cda5da8dSAndroid Build Coastguard Worker if func is not None: 419*cda5da8dSAndroid Build Coastguard Worker return func(*params) 420*cda5da8dSAndroid Build Coastguard Worker 421*cda5da8dSAndroid Build Coastguard Worker raise Exception('method "%s" is not supported' % method) 422*cda5da8dSAndroid Build Coastguard Worker 423*cda5da8dSAndroid Build Coastguard Workerclass SimpleXMLRPCRequestHandler(BaseHTTPRequestHandler): 424*cda5da8dSAndroid Build Coastguard Worker """Simple XML-RPC request handler class. 425*cda5da8dSAndroid Build Coastguard Worker 426*cda5da8dSAndroid Build Coastguard Worker Handles all HTTP POST requests and attempts to decode them as 427*cda5da8dSAndroid Build Coastguard Worker XML-RPC requests. 428*cda5da8dSAndroid Build Coastguard Worker """ 429*cda5da8dSAndroid Build Coastguard Worker 430*cda5da8dSAndroid Build Coastguard Worker # Class attribute listing the accessible path components; 431*cda5da8dSAndroid Build Coastguard Worker # paths not on this list will result in a 404 error. 432*cda5da8dSAndroid Build Coastguard Worker rpc_paths = ('/', '/RPC2', '/pydoc.css') 433*cda5da8dSAndroid Build Coastguard Worker 434*cda5da8dSAndroid Build Coastguard Worker #if not None, encode responses larger than this, if possible 435*cda5da8dSAndroid Build Coastguard Worker encode_threshold = 1400 #a common MTU 436*cda5da8dSAndroid Build Coastguard Worker 437*cda5da8dSAndroid Build Coastguard Worker #Override form StreamRequestHandler: full buffering of output 438*cda5da8dSAndroid Build Coastguard Worker #and no Nagle. 439*cda5da8dSAndroid Build Coastguard Worker wbufsize = -1 440*cda5da8dSAndroid Build Coastguard Worker disable_nagle_algorithm = True 441*cda5da8dSAndroid Build Coastguard Worker 442*cda5da8dSAndroid Build Coastguard Worker # a re to match a gzip Accept-Encoding 443*cda5da8dSAndroid Build Coastguard Worker aepattern = re.compile(r""" 444*cda5da8dSAndroid Build Coastguard Worker \s* ([^\s;]+) \s* #content-coding 445*cda5da8dSAndroid Build Coastguard Worker (;\s* q \s*=\s* ([0-9\.]+))? #q 446*cda5da8dSAndroid Build Coastguard Worker """, re.VERBOSE | re.IGNORECASE) 447*cda5da8dSAndroid Build Coastguard Worker 448*cda5da8dSAndroid Build Coastguard Worker def accept_encodings(self): 449*cda5da8dSAndroid Build Coastguard Worker r = {} 450*cda5da8dSAndroid Build Coastguard Worker ae = self.headers.get("Accept-Encoding", "") 451*cda5da8dSAndroid Build Coastguard Worker for e in ae.split(","): 452*cda5da8dSAndroid Build Coastguard Worker match = self.aepattern.match(e) 453*cda5da8dSAndroid Build Coastguard Worker if match: 454*cda5da8dSAndroid Build Coastguard Worker v = match.group(3) 455*cda5da8dSAndroid Build Coastguard Worker v = float(v) if v else 1.0 456*cda5da8dSAndroid Build Coastguard Worker r[match.group(1)] = v 457*cda5da8dSAndroid Build Coastguard Worker return r 458*cda5da8dSAndroid Build Coastguard Worker 459*cda5da8dSAndroid Build Coastguard Worker def is_rpc_path_valid(self): 460*cda5da8dSAndroid Build Coastguard Worker if self.rpc_paths: 461*cda5da8dSAndroid Build Coastguard Worker return self.path in self.rpc_paths 462*cda5da8dSAndroid Build Coastguard Worker else: 463*cda5da8dSAndroid Build Coastguard Worker # If .rpc_paths is empty, just assume all paths are legal 464*cda5da8dSAndroid Build Coastguard Worker return True 465*cda5da8dSAndroid Build Coastguard Worker 466*cda5da8dSAndroid Build Coastguard Worker def do_POST(self): 467*cda5da8dSAndroid Build Coastguard Worker """Handles the HTTP POST request. 468*cda5da8dSAndroid Build Coastguard Worker 469*cda5da8dSAndroid Build Coastguard Worker Attempts to interpret all HTTP POST requests as XML-RPC calls, 470*cda5da8dSAndroid Build Coastguard Worker which are forwarded to the server's _dispatch method for handling. 471*cda5da8dSAndroid Build Coastguard Worker """ 472*cda5da8dSAndroid Build Coastguard Worker 473*cda5da8dSAndroid Build Coastguard Worker # Check that the path is legal 474*cda5da8dSAndroid Build Coastguard Worker if not self.is_rpc_path_valid(): 475*cda5da8dSAndroid Build Coastguard Worker self.report_404() 476*cda5da8dSAndroid Build Coastguard Worker return 477*cda5da8dSAndroid Build Coastguard Worker 478*cda5da8dSAndroid Build Coastguard Worker try: 479*cda5da8dSAndroid Build Coastguard Worker # Get arguments by reading body of request. 480*cda5da8dSAndroid Build Coastguard Worker # We read this in chunks to avoid straining 481*cda5da8dSAndroid Build Coastguard Worker # socket.read(); around the 10 or 15Mb mark, some platforms 482*cda5da8dSAndroid Build Coastguard Worker # begin to have problems (bug #792570). 483*cda5da8dSAndroid Build Coastguard Worker max_chunk_size = 10*1024*1024 484*cda5da8dSAndroid Build Coastguard Worker size_remaining = int(self.headers["content-length"]) 485*cda5da8dSAndroid Build Coastguard Worker L = [] 486*cda5da8dSAndroid Build Coastguard Worker while size_remaining: 487*cda5da8dSAndroid Build Coastguard Worker chunk_size = min(size_remaining, max_chunk_size) 488*cda5da8dSAndroid Build Coastguard Worker chunk = self.rfile.read(chunk_size) 489*cda5da8dSAndroid Build Coastguard Worker if not chunk: 490*cda5da8dSAndroid Build Coastguard Worker break 491*cda5da8dSAndroid Build Coastguard Worker L.append(chunk) 492*cda5da8dSAndroid Build Coastguard Worker size_remaining -= len(L[-1]) 493*cda5da8dSAndroid Build Coastguard Worker data = b''.join(L) 494*cda5da8dSAndroid Build Coastguard Worker 495*cda5da8dSAndroid Build Coastguard Worker data = self.decode_request_content(data) 496*cda5da8dSAndroid Build Coastguard Worker if data is None: 497*cda5da8dSAndroid Build Coastguard Worker return #response has been sent 498*cda5da8dSAndroid Build Coastguard Worker 499*cda5da8dSAndroid Build Coastguard Worker # In previous versions of SimpleXMLRPCServer, _dispatch 500*cda5da8dSAndroid Build Coastguard Worker # could be overridden in this class, instead of in 501*cda5da8dSAndroid Build Coastguard Worker # SimpleXMLRPCDispatcher. To maintain backwards compatibility, 502*cda5da8dSAndroid Build Coastguard Worker # check to see if a subclass implements _dispatch and dispatch 503*cda5da8dSAndroid Build Coastguard Worker # using that method if present. 504*cda5da8dSAndroid Build Coastguard Worker response = self.server._marshaled_dispatch( 505*cda5da8dSAndroid Build Coastguard Worker data, getattr(self, '_dispatch', None), self.path 506*cda5da8dSAndroid Build Coastguard Worker ) 507*cda5da8dSAndroid Build Coastguard Worker except Exception as e: # This should only happen if the module is buggy 508*cda5da8dSAndroid Build Coastguard Worker # internal error, report as HTTP server error 509*cda5da8dSAndroid Build Coastguard Worker self.send_response(500) 510*cda5da8dSAndroid Build Coastguard Worker 511*cda5da8dSAndroid Build Coastguard Worker # Send information about the exception if requested 512*cda5da8dSAndroid Build Coastguard Worker if hasattr(self.server, '_send_traceback_header') and \ 513*cda5da8dSAndroid Build Coastguard Worker self.server._send_traceback_header: 514*cda5da8dSAndroid Build Coastguard Worker self.send_header("X-exception", str(e)) 515*cda5da8dSAndroid Build Coastguard Worker trace = traceback.format_exc() 516*cda5da8dSAndroid Build Coastguard Worker trace = str(trace.encode('ASCII', 'backslashreplace'), 'ASCII') 517*cda5da8dSAndroid Build Coastguard Worker self.send_header("X-traceback", trace) 518*cda5da8dSAndroid Build Coastguard Worker 519*cda5da8dSAndroid Build Coastguard Worker self.send_header("Content-length", "0") 520*cda5da8dSAndroid Build Coastguard Worker self.end_headers() 521*cda5da8dSAndroid Build Coastguard Worker else: 522*cda5da8dSAndroid Build Coastguard Worker self.send_response(200) 523*cda5da8dSAndroid Build Coastguard Worker self.send_header("Content-type", "text/xml") 524*cda5da8dSAndroid Build Coastguard Worker if self.encode_threshold is not None: 525*cda5da8dSAndroid Build Coastguard Worker if len(response) > self.encode_threshold: 526*cda5da8dSAndroid Build Coastguard Worker q = self.accept_encodings().get("gzip", 0) 527*cda5da8dSAndroid Build Coastguard Worker if q: 528*cda5da8dSAndroid Build Coastguard Worker try: 529*cda5da8dSAndroid Build Coastguard Worker response = gzip_encode(response) 530*cda5da8dSAndroid Build Coastguard Worker self.send_header("Content-Encoding", "gzip") 531*cda5da8dSAndroid Build Coastguard Worker except NotImplementedError: 532*cda5da8dSAndroid Build Coastguard Worker pass 533*cda5da8dSAndroid Build Coastguard Worker self.send_header("Content-length", str(len(response))) 534*cda5da8dSAndroid Build Coastguard Worker self.end_headers() 535*cda5da8dSAndroid Build Coastguard Worker self.wfile.write(response) 536*cda5da8dSAndroid Build Coastguard Worker 537*cda5da8dSAndroid Build Coastguard Worker def decode_request_content(self, data): 538*cda5da8dSAndroid Build Coastguard Worker #support gzip encoding of request 539*cda5da8dSAndroid Build Coastguard Worker encoding = self.headers.get("content-encoding", "identity").lower() 540*cda5da8dSAndroid Build Coastguard Worker if encoding == "identity": 541*cda5da8dSAndroid Build Coastguard Worker return data 542*cda5da8dSAndroid Build Coastguard Worker if encoding == "gzip": 543*cda5da8dSAndroid Build Coastguard Worker try: 544*cda5da8dSAndroid Build Coastguard Worker return gzip_decode(data) 545*cda5da8dSAndroid Build Coastguard Worker except NotImplementedError: 546*cda5da8dSAndroid Build Coastguard Worker self.send_response(501, "encoding %r not supported" % encoding) 547*cda5da8dSAndroid Build Coastguard Worker except ValueError: 548*cda5da8dSAndroid Build Coastguard Worker self.send_response(400, "error decoding gzip content") 549*cda5da8dSAndroid Build Coastguard Worker else: 550*cda5da8dSAndroid Build Coastguard Worker self.send_response(501, "encoding %r not supported" % encoding) 551*cda5da8dSAndroid Build Coastguard Worker self.send_header("Content-length", "0") 552*cda5da8dSAndroid Build Coastguard Worker self.end_headers() 553*cda5da8dSAndroid Build Coastguard Worker 554*cda5da8dSAndroid Build Coastguard Worker def report_404 (self): 555*cda5da8dSAndroid Build Coastguard Worker # Report a 404 error 556*cda5da8dSAndroid Build Coastguard Worker self.send_response(404) 557*cda5da8dSAndroid Build Coastguard Worker response = b'No such page' 558*cda5da8dSAndroid Build Coastguard Worker self.send_header("Content-type", "text/plain") 559*cda5da8dSAndroid Build Coastguard Worker self.send_header("Content-length", str(len(response))) 560*cda5da8dSAndroid Build Coastguard Worker self.end_headers() 561*cda5da8dSAndroid Build Coastguard Worker self.wfile.write(response) 562*cda5da8dSAndroid Build Coastguard Worker 563*cda5da8dSAndroid Build Coastguard Worker def log_request(self, code='-', size='-'): 564*cda5da8dSAndroid Build Coastguard Worker """Selectively log an accepted request.""" 565*cda5da8dSAndroid Build Coastguard Worker 566*cda5da8dSAndroid Build Coastguard Worker if self.server.logRequests: 567*cda5da8dSAndroid Build Coastguard Worker BaseHTTPRequestHandler.log_request(self, code, size) 568*cda5da8dSAndroid Build Coastguard Worker 569*cda5da8dSAndroid Build Coastguard Workerclass SimpleXMLRPCServer(socketserver.TCPServer, 570*cda5da8dSAndroid Build Coastguard Worker SimpleXMLRPCDispatcher): 571*cda5da8dSAndroid Build Coastguard Worker """Simple XML-RPC server. 572*cda5da8dSAndroid Build Coastguard Worker 573*cda5da8dSAndroid Build Coastguard Worker Simple XML-RPC server that allows functions and a single instance 574*cda5da8dSAndroid Build Coastguard Worker to be installed to handle requests. The default implementation 575*cda5da8dSAndroid Build Coastguard Worker attempts to dispatch XML-RPC calls to the functions or instance 576*cda5da8dSAndroid Build Coastguard Worker installed in the server. Override the _dispatch method inherited 577*cda5da8dSAndroid Build Coastguard Worker from SimpleXMLRPCDispatcher to change this behavior. 578*cda5da8dSAndroid Build Coastguard Worker """ 579*cda5da8dSAndroid Build Coastguard Worker 580*cda5da8dSAndroid Build Coastguard Worker allow_reuse_address = True 581*cda5da8dSAndroid Build Coastguard Worker 582*cda5da8dSAndroid Build Coastguard Worker # Warning: this is for debugging purposes only! Never set this to True in 583*cda5da8dSAndroid Build Coastguard Worker # production code, as will be sending out sensitive information (exception 584*cda5da8dSAndroid Build Coastguard Worker # and stack trace details) when exceptions are raised inside 585*cda5da8dSAndroid Build Coastguard Worker # SimpleXMLRPCRequestHandler.do_POST 586*cda5da8dSAndroid Build Coastguard Worker _send_traceback_header = False 587*cda5da8dSAndroid Build Coastguard Worker 588*cda5da8dSAndroid Build Coastguard Worker def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, 589*cda5da8dSAndroid Build Coastguard Worker logRequests=True, allow_none=False, encoding=None, 590*cda5da8dSAndroid Build Coastguard Worker bind_and_activate=True, use_builtin_types=False): 591*cda5da8dSAndroid Build Coastguard Worker self.logRequests = logRequests 592*cda5da8dSAndroid Build Coastguard Worker 593*cda5da8dSAndroid Build Coastguard Worker SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types) 594*cda5da8dSAndroid Build Coastguard Worker socketserver.TCPServer.__init__(self, addr, requestHandler, bind_and_activate) 595*cda5da8dSAndroid Build Coastguard Worker 596*cda5da8dSAndroid Build Coastguard Worker 597*cda5da8dSAndroid Build Coastguard Workerclass MultiPathXMLRPCServer(SimpleXMLRPCServer): 598*cda5da8dSAndroid Build Coastguard Worker """Multipath XML-RPC Server 599*cda5da8dSAndroid Build Coastguard Worker This specialization of SimpleXMLRPCServer allows the user to create 600*cda5da8dSAndroid Build Coastguard Worker multiple Dispatcher instances and assign them to different 601*cda5da8dSAndroid Build Coastguard Worker HTTP request paths. This makes it possible to run two or more 602*cda5da8dSAndroid Build Coastguard Worker 'virtual XML-RPC servers' at the same port. 603*cda5da8dSAndroid Build Coastguard Worker Make sure that the requestHandler accepts the paths in question. 604*cda5da8dSAndroid Build Coastguard Worker """ 605*cda5da8dSAndroid Build Coastguard Worker def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, 606*cda5da8dSAndroid Build Coastguard Worker logRequests=True, allow_none=False, encoding=None, 607*cda5da8dSAndroid Build Coastguard Worker bind_and_activate=True, use_builtin_types=False): 608*cda5da8dSAndroid Build Coastguard Worker 609*cda5da8dSAndroid Build Coastguard Worker SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, allow_none, 610*cda5da8dSAndroid Build Coastguard Worker encoding, bind_and_activate, use_builtin_types) 611*cda5da8dSAndroid Build Coastguard Worker self.dispatchers = {} 612*cda5da8dSAndroid Build Coastguard Worker self.allow_none = allow_none 613*cda5da8dSAndroid Build Coastguard Worker self.encoding = encoding or 'utf-8' 614*cda5da8dSAndroid Build Coastguard Worker 615*cda5da8dSAndroid Build Coastguard Worker def add_dispatcher(self, path, dispatcher): 616*cda5da8dSAndroid Build Coastguard Worker self.dispatchers[path] = dispatcher 617*cda5da8dSAndroid Build Coastguard Worker return dispatcher 618*cda5da8dSAndroid Build Coastguard Worker 619*cda5da8dSAndroid Build Coastguard Worker def get_dispatcher(self, path): 620*cda5da8dSAndroid Build Coastguard Worker return self.dispatchers[path] 621*cda5da8dSAndroid Build Coastguard Worker 622*cda5da8dSAndroid Build Coastguard Worker def _marshaled_dispatch(self, data, dispatch_method = None, path = None): 623*cda5da8dSAndroid Build Coastguard Worker try: 624*cda5da8dSAndroid Build Coastguard Worker response = self.dispatchers[path]._marshaled_dispatch( 625*cda5da8dSAndroid Build Coastguard Worker data, dispatch_method, path) 626*cda5da8dSAndroid Build Coastguard Worker except BaseException as exc: 627*cda5da8dSAndroid Build Coastguard Worker # report low level exception back to server 628*cda5da8dSAndroid Build Coastguard Worker # (each dispatcher should have handled their own 629*cda5da8dSAndroid Build Coastguard Worker # exceptions) 630*cda5da8dSAndroid Build Coastguard Worker response = dumps( 631*cda5da8dSAndroid Build Coastguard Worker Fault(1, "%s:%s" % (type(exc), exc)), 632*cda5da8dSAndroid Build Coastguard Worker encoding=self.encoding, allow_none=self.allow_none) 633*cda5da8dSAndroid Build Coastguard Worker response = response.encode(self.encoding, 'xmlcharrefreplace') 634*cda5da8dSAndroid Build Coastguard Worker return response 635*cda5da8dSAndroid Build Coastguard Worker 636*cda5da8dSAndroid Build Coastguard Workerclass CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher): 637*cda5da8dSAndroid Build Coastguard Worker """Simple handler for XML-RPC data passed through CGI.""" 638*cda5da8dSAndroid Build Coastguard Worker 639*cda5da8dSAndroid Build Coastguard Worker def __init__(self, allow_none=False, encoding=None, use_builtin_types=False): 640*cda5da8dSAndroid Build Coastguard Worker SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types) 641*cda5da8dSAndroid Build Coastguard Worker 642*cda5da8dSAndroid Build Coastguard Worker def handle_xmlrpc(self, request_text): 643*cda5da8dSAndroid Build Coastguard Worker """Handle a single XML-RPC request""" 644*cda5da8dSAndroid Build Coastguard Worker 645*cda5da8dSAndroid Build Coastguard Worker response = self._marshaled_dispatch(request_text) 646*cda5da8dSAndroid Build Coastguard Worker 647*cda5da8dSAndroid Build Coastguard Worker print('Content-Type: text/xml') 648*cda5da8dSAndroid Build Coastguard Worker print('Content-Length: %d' % len(response)) 649*cda5da8dSAndroid Build Coastguard Worker print() 650*cda5da8dSAndroid Build Coastguard Worker sys.stdout.flush() 651*cda5da8dSAndroid Build Coastguard Worker sys.stdout.buffer.write(response) 652*cda5da8dSAndroid Build Coastguard Worker sys.stdout.buffer.flush() 653*cda5da8dSAndroid Build Coastguard Worker 654*cda5da8dSAndroid Build Coastguard Worker def handle_get(self): 655*cda5da8dSAndroid Build Coastguard Worker """Handle a single HTTP GET request. 656*cda5da8dSAndroid Build Coastguard Worker 657*cda5da8dSAndroid Build Coastguard Worker Default implementation indicates an error because 658*cda5da8dSAndroid Build Coastguard Worker XML-RPC uses the POST method. 659*cda5da8dSAndroid Build Coastguard Worker """ 660*cda5da8dSAndroid Build Coastguard Worker 661*cda5da8dSAndroid Build Coastguard Worker code = 400 662*cda5da8dSAndroid Build Coastguard Worker message, explain = BaseHTTPRequestHandler.responses[code] 663*cda5da8dSAndroid Build Coastguard Worker 664*cda5da8dSAndroid Build Coastguard Worker response = http.server.DEFAULT_ERROR_MESSAGE % \ 665*cda5da8dSAndroid Build Coastguard Worker { 666*cda5da8dSAndroid Build Coastguard Worker 'code' : code, 667*cda5da8dSAndroid Build Coastguard Worker 'message' : message, 668*cda5da8dSAndroid Build Coastguard Worker 'explain' : explain 669*cda5da8dSAndroid Build Coastguard Worker } 670*cda5da8dSAndroid Build Coastguard Worker response = response.encode('utf-8') 671*cda5da8dSAndroid Build Coastguard Worker print('Status: %d %s' % (code, message)) 672*cda5da8dSAndroid Build Coastguard Worker print('Content-Type: %s' % http.server.DEFAULT_ERROR_CONTENT_TYPE) 673*cda5da8dSAndroid Build Coastguard Worker print('Content-Length: %d' % len(response)) 674*cda5da8dSAndroid Build Coastguard Worker print() 675*cda5da8dSAndroid Build Coastguard Worker sys.stdout.flush() 676*cda5da8dSAndroid Build Coastguard Worker sys.stdout.buffer.write(response) 677*cda5da8dSAndroid Build Coastguard Worker sys.stdout.buffer.flush() 678*cda5da8dSAndroid Build Coastguard Worker 679*cda5da8dSAndroid Build Coastguard Worker def handle_request(self, request_text=None): 680*cda5da8dSAndroid Build Coastguard Worker """Handle a single XML-RPC request passed through a CGI post method. 681*cda5da8dSAndroid Build Coastguard Worker 682*cda5da8dSAndroid Build Coastguard Worker If no XML data is given then it is read from stdin. The resulting 683*cda5da8dSAndroid Build Coastguard Worker XML-RPC response is printed to stdout along with the correct HTTP 684*cda5da8dSAndroid Build Coastguard Worker headers. 685*cda5da8dSAndroid Build Coastguard Worker """ 686*cda5da8dSAndroid Build Coastguard Worker 687*cda5da8dSAndroid Build Coastguard Worker if request_text is None and \ 688*cda5da8dSAndroid Build Coastguard Worker os.environ.get('REQUEST_METHOD', None) == 'GET': 689*cda5da8dSAndroid Build Coastguard Worker self.handle_get() 690*cda5da8dSAndroid Build Coastguard Worker else: 691*cda5da8dSAndroid Build Coastguard Worker # POST data is normally available through stdin 692*cda5da8dSAndroid Build Coastguard Worker try: 693*cda5da8dSAndroid Build Coastguard Worker length = int(os.environ.get('CONTENT_LENGTH', None)) 694*cda5da8dSAndroid Build Coastguard Worker except (ValueError, TypeError): 695*cda5da8dSAndroid Build Coastguard Worker length = -1 696*cda5da8dSAndroid Build Coastguard Worker if request_text is None: 697*cda5da8dSAndroid Build Coastguard Worker request_text = sys.stdin.read(length) 698*cda5da8dSAndroid Build Coastguard Worker 699*cda5da8dSAndroid Build Coastguard Worker self.handle_xmlrpc(request_text) 700*cda5da8dSAndroid Build Coastguard Worker 701*cda5da8dSAndroid Build Coastguard Worker 702*cda5da8dSAndroid Build Coastguard Worker# ----------------------------------------------------------------------------- 703*cda5da8dSAndroid Build Coastguard Worker# Self documenting XML-RPC Server. 704*cda5da8dSAndroid Build Coastguard Worker 705*cda5da8dSAndroid Build Coastguard Workerclass ServerHTMLDoc(pydoc.HTMLDoc): 706*cda5da8dSAndroid Build Coastguard Worker """Class used to generate pydoc HTML document for a server""" 707*cda5da8dSAndroid Build Coastguard Worker 708*cda5da8dSAndroid Build Coastguard Worker def markup(self, text, escape=None, funcs={}, classes={}, methods={}): 709*cda5da8dSAndroid Build Coastguard Worker """Mark up some plain text, given a context of symbols to look for. 710*cda5da8dSAndroid Build Coastguard Worker Each context dictionary maps object names to anchor names.""" 711*cda5da8dSAndroid Build Coastguard Worker escape = escape or self.escape 712*cda5da8dSAndroid Build Coastguard Worker results = [] 713*cda5da8dSAndroid Build Coastguard Worker here = 0 714*cda5da8dSAndroid Build Coastguard Worker 715*cda5da8dSAndroid Build Coastguard Worker # XXX Note that this regular expression does not allow for the 716*cda5da8dSAndroid Build Coastguard Worker # hyperlinking of arbitrary strings being used as method 717*cda5da8dSAndroid Build Coastguard Worker # names. Only methods with names consisting of word characters 718*cda5da8dSAndroid Build Coastguard Worker # and '.'s are hyperlinked. 719*cda5da8dSAndroid Build Coastguard Worker pattern = re.compile(r'\b((http|https|ftp)://\S+[\w/]|' 720*cda5da8dSAndroid Build Coastguard Worker r'RFC[- ]?(\d+)|' 721*cda5da8dSAndroid Build Coastguard Worker r'PEP[- ]?(\d+)|' 722*cda5da8dSAndroid Build Coastguard Worker r'(self\.)?((?:\w|\.)+))\b') 723*cda5da8dSAndroid Build Coastguard Worker while 1: 724*cda5da8dSAndroid Build Coastguard Worker match = pattern.search(text, here) 725*cda5da8dSAndroid Build Coastguard Worker if not match: break 726*cda5da8dSAndroid Build Coastguard Worker start, end = match.span() 727*cda5da8dSAndroid Build Coastguard Worker results.append(escape(text[here:start])) 728*cda5da8dSAndroid Build Coastguard Worker 729*cda5da8dSAndroid Build Coastguard Worker all, scheme, rfc, pep, selfdot, name = match.groups() 730*cda5da8dSAndroid Build Coastguard Worker if scheme: 731*cda5da8dSAndroid Build Coastguard Worker url = escape(all).replace('"', '"') 732*cda5da8dSAndroid Build Coastguard Worker results.append('<a href="%s">%s</a>' % (url, url)) 733*cda5da8dSAndroid Build Coastguard Worker elif rfc: 734*cda5da8dSAndroid Build Coastguard Worker url = 'https://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc) 735*cda5da8dSAndroid Build Coastguard Worker results.append('<a href="%s">%s</a>' % (url, escape(all))) 736*cda5da8dSAndroid Build Coastguard Worker elif pep: 737*cda5da8dSAndroid Build Coastguard Worker url = 'https://peps.python.org/pep-%04d/' % int(pep) 738*cda5da8dSAndroid Build Coastguard Worker results.append('<a href="%s">%s</a>' % (url, escape(all))) 739*cda5da8dSAndroid Build Coastguard Worker elif text[end:end+1] == '(': 740*cda5da8dSAndroid Build Coastguard Worker results.append(self.namelink(name, methods, funcs, classes)) 741*cda5da8dSAndroid Build Coastguard Worker elif selfdot: 742*cda5da8dSAndroid Build Coastguard Worker results.append('self.<strong>%s</strong>' % name) 743*cda5da8dSAndroid Build Coastguard Worker else: 744*cda5da8dSAndroid Build Coastguard Worker results.append(self.namelink(name, classes)) 745*cda5da8dSAndroid Build Coastguard Worker here = end 746*cda5da8dSAndroid Build Coastguard Worker results.append(escape(text[here:])) 747*cda5da8dSAndroid Build Coastguard Worker return ''.join(results) 748*cda5da8dSAndroid Build Coastguard Worker 749*cda5da8dSAndroid Build Coastguard Worker def docroutine(self, object, name, mod=None, 750*cda5da8dSAndroid Build Coastguard Worker funcs={}, classes={}, methods={}, cl=None): 751*cda5da8dSAndroid Build Coastguard Worker """Produce HTML documentation for a function or method object.""" 752*cda5da8dSAndroid Build Coastguard Worker 753*cda5da8dSAndroid Build Coastguard Worker anchor = (cl and cl.__name__ or '') + '-' + name 754*cda5da8dSAndroid Build Coastguard Worker note = '' 755*cda5da8dSAndroid Build Coastguard Worker 756*cda5da8dSAndroid Build Coastguard Worker title = '<a name="%s"><strong>%s</strong></a>' % ( 757*cda5da8dSAndroid Build Coastguard Worker self.escape(anchor), self.escape(name)) 758*cda5da8dSAndroid Build Coastguard Worker 759*cda5da8dSAndroid Build Coastguard Worker if callable(object): 760*cda5da8dSAndroid Build Coastguard Worker argspec = str(signature(object)) 761*cda5da8dSAndroid Build Coastguard Worker else: 762*cda5da8dSAndroid Build Coastguard Worker argspec = '(...)' 763*cda5da8dSAndroid Build Coastguard Worker 764*cda5da8dSAndroid Build Coastguard Worker if isinstance(object, tuple): 765*cda5da8dSAndroid Build Coastguard Worker argspec = object[0] or argspec 766*cda5da8dSAndroid Build Coastguard Worker docstring = object[1] or "" 767*cda5da8dSAndroid Build Coastguard Worker else: 768*cda5da8dSAndroid Build Coastguard Worker docstring = pydoc.getdoc(object) 769*cda5da8dSAndroid Build Coastguard Worker 770*cda5da8dSAndroid Build Coastguard Worker decl = title + argspec + (note and self.grey( 771*cda5da8dSAndroid Build Coastguard Worker '<font face="helvetica, arial">%s</font>' % note)) 772*cda5da8dSAndroid Build Coastguard Worker 773*cda5da8dSAndroid Build Coastguard Worker doc = self.markup( 774*cda5da8dSAndroid Build Coastguard Worker docstring, self.preformat, funcs, classes, methods) 775*cda5da8dSAndroid Build Coastguard Worker doc = doc and '<dd><tt>%s</tt></dd>' % doc 776*cda5da8dSAndroid Build Coastguard Worker return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc) 777*cda5da8dSAndroid Build Coastguard Worker 778*cda5da8dSAndroid Build Coastguard Worker def docserver(self, server_name, package_documentation, methods): 779*cda5da8dSAndroid Build Coastguard Worker """Produce HTML documentation for an XML-RPC server.""" 780*cda5da8dSAndroid Build Coastguard Worker 781*cda5da8dSAndroid Build Coastguard Worker fdict = {} 782*cda5da8dSAndroid Build Coastguard Worker for key, value in methods.items(): 783*cda5da8dSAndroid Build Coastguard Worker fdict[key] = '#-' + key 784*cda5da8dSAndroid Build Coastguard Worker fdict[value] = fdict[key] 785*cda5da8dSAndroid Build Coastguard Worker 786*cda5da8dSAndroid Build Coastguard Worker server_name = self.escape(server_name) 787*cda5da8dSAndroid Build Coastguard Worker head = '<big><big><strong>%s</strong></big></big>' % server_name 788*cda5da8dSAndroid Build Coastguard Worker result = self.heading(head) 789*cda5da8dSAndroid Build Coastguard Worker 790*cda5da8dSAndroid Build Coastguard Worker doc = self.markup(package_documentation, self.preformat, fdict) 791*cda5da8dSAndroid Build Coastguard Worker doc = doc and '<tt>%s</tt>' % doc 792*cda5da8dSAndroid Build Coastguard Worker result = result + '<p>%s</p>\n' % doc 793*cda5da8dSAndroid Build Coastguard Worker 794*cda5da8dSAndroid Build Coastguard Worker contents = [] 795*cda5da8dSAndroid Build Coastguard Worker method_items = sorted(methods.items()) 796*cda5da8dSAndroid Build Coastguard Worker for key, value in method_items: 797*cda5da8dSAndroid Build Coastguard Worker contents.append(self.docroutine(value, key, funcs=fdict)) 798*cda5da8dSAndroid Build Coastguard Worker result = result + self.bigsection( 799*cda5da8dSAndroid Build Coastguard Worker 'Methods', 'functions', ''.join(contents)) 800*cda5da8dSAndroid Build Coastguard Worker 801*cda5da8dSAndroid Build Coastguard Worker return result 802*cda5da8dSAndroid Build Coastguard Worker 803*cda5da8dSAndroid Build Coastguard Worker 804*cda5da8dSAndroid Build Coastguard Worker def page(self, title, contents): 805*cda5da8dSAndroid Build Coastguard Worker """Format an HTML page.""" 806*cda5da8dSAndroid Build Coastguard Worker css_path = "/pydoc.css" 807*cda5da8dSAndroid Build Coastguard Worker css_link = ( 808*cda5da8dSAndroid Build Coastguard Worker '<link rel="stylesheet" type="text/css" href="%s">' % 809*cda5da8dSAndroid Build Coastguard Worker css_path) 810*cda5da8dSAndroid Build Coastguard Worker return '''\ 811*cda5da8dSAndroid Build Coastguard Worker<!DOCTYPE> 812*cda5da8dSAndroid Build Coastguard Worker<html lang="en"> 813*cda5da8dSAndroid Build Coastguard Worker<head> 814*cda5da8dSAndroid Build Coastguard Worker<meta charset="utf-8"> 815*cda5da8dSAndroid Build Coastguard Worker<title>Python: %s</title> 816*cda5da8dSAndroid Build Coastguard Worker%s</head><body>%s</body></html>''' % (title, css_link, contents) 817*cda5da8dSAndroid Build Coastguard Worker 818*cda5da8dSAndroid Build Coastguard Workerclass XMLRPCDocGenerator: 819*cda5da8dSAndroid Build Coastguard Worker """Generates documentation for an XML-RPC server. 820*cda5da8dSAndroid Build Coastguard Worker 821*cda5da8dSAndroid Build Coastguard Worker This class is designed as mix-in and should not 822*cda5da8dSAndroid Build Coastguard Worker be constructed directly. 823*cda5da8dSAndroid Build Coastguard Worker """ 824*cda5da8dSAndroid Build Coastguard Worker 825*cda5da8dSAndroid Build Coastguard Worker def __init__(self): 826*cda5da8dSAndroid Build Coastguard Worker # setup variables used for HTML documentation 827*cda5da8dSAndroid Build Coastguard Worker self.server_name = 'XML-RPC Server Documentation' 828*cda5da8dSAndroid Build Coastguard Worker self.server_documentation = \ 829*cda5da8dSAndroid Build Coastguard Worker "This server exports the following methods through the XML-RPC "\ 830*cda5da8dSAndroid Build Coastguard Worker "protocol." 831*cda5da8dSAndroid Build Coastguard Worker self.server_title = 'XML-RPC Server Documentation' 832*cda5da8dSAndroid Build Coastguard Worker 833*cda5da8dSAndroid Build Coastguard Worker def set_server_title(self, server_title): 834*cda5da8dSAndroid Build Coastguard Worker """Set the HTML title of the generated server documentation""" 835*cda5da8dSAndroid Build Coastguard Worker 836*cda5da8dSAndroid Build Coastguard Worker self.server_title = server_title 837*cda5da8dSAndroid Build Coastguard Worker 838*cda5da8dSAndroid Build Coastguard Worker def set_server_name(self, server_name): 839*cda5da8dSAndroid Build Coastguard Worker """Set the name of the generated HTML server documentation""" 840*cda5da8dSAndroid Build Coastguard Worker 841*cda5da8dSAndroid Build Coastguard Worker self.server_name = server_name 842*cda5da8dSAndroid Build Coastguard Worker 843*cda5da8dSAndroid Build Coastguard Worker def set_server_documentation(self, server_documentation): 844*cda5da8dSAndroid Build Coastguard Worker """Set the documentation string for the entire server.""" 845*cda5da8dSAndroid Build Coastguard Worker 846*cda5da8dSAndroid Build Coastguard Worker self.server_documentation = server_documentation 847*cda5da8dSAndroid Build Coastguard Worker 848*cda5da8dSAndroid Build Coastguard Worker def generate_html_documentation(self): 849*cda5da8dSAndroid Build Coastguard Worker """generate_html_documentation() => html documentation for the server 850*cda5da8dSAndroid Build Coastguard Worker 851*cda5da8dSAndroid Build Coastguard Worker Generates HTML documentation for the server using introspection for 852*cda5da8dSAndroid Build Coastguard Worker installed functions and instances that do not implement the 853*cda5da8dSAndroid Build Coastguard Worker _dispatch method. Alternatively, instances can choose to implement 854*cda5da8dSAndroid Build Coastguard Worker the _get_method_argstring(method_name) method to provide the 855*cda5da8dSAndroid Build Coastguard Worker argument string used in the documentation and the 856*cda5da8dSAndroid Build Coastguard Worker _methodHelp(method_name) method to provide the help text used 857*cda5da8dSAndroid Build Coastguard Worker in the documentation.""" 858*cda5da8dSAndroid Build Coastguard Worker 859*cda5da8dSAndroid Build Coastguard Worker methods = {} 860*cda5da8dSAndroid Build Coastguard Worker 861*cda5da8dSAndroid Build Coastguard Worker for method_name in self.system_listMethods(): 862*cda5da8dSAndroid Build Coastguard Worker if method_name in self.funcs: 863*cda5da8dSAndroid Build Coastguard Worker method = self.funcs[method_name] 864*cda5da8dSAndroid Build Coastguard Worker elif self.instance is not None: 865*cda5da8dSAndroid Build Coastguard Worker method_info = [None, None] # argspec, documentation 866*cda5da8dSAndroid Build Coastguard Worker if hasattr(self.instance, '_get_method_argstring'): 867*cda5da8dSAndroid Build Coastguard Worker method_info[0] = self.instance._get_method_argstring(method_name) 868*cda5da8dSAndroid Build Coastguard Worker if hasattr(self.instance, '_methodHelp'): 869*cda5da8dSAndroid Build Coastguard Worker method_info[1] = self.instance._methodHelp(method_name) 870*cda5da8dSAndroid Build Coastguard Worker 871*cda5da8dSAndroid Build Coastguard Worker method_info = tuple(method_info) 872*cda5da8dSAndroid Build Coastguard Worker if method_info != (None, None): 873*cda5da8dSAndroid Build Coastguard Worker method = method_info 874*cda5da8dSAndroid Build Coastguard Worker elif not hasattr(self.instance, '_dispatch'): 875*cda5da8dSAndroid Build Coastguard Worker try: 876*cda5da8dSAndroid Build Coastguard Worker method = resolve_dotted_attribute( 877*cda5da8dSAndroid Build Coastguard Worker self.instance, 878*cda5da8dSAndroid Build Coastguard Worker method_name 879*cda5da8dSAndroid Build Coastguard Worker ) 880*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 881*cda5da8dSAndroid Build Coastguard Worker method = method_info 882*cda5da8dSAndroid Build Coastguard Worker else: 883*cda5da8dSAndroid Build Coastguard Worker method = method_info 884*cda5da8dSAndroid Build Coastguard Worker else: 885*cda5da8dSAndroid Build Coastguard Worker assert 0, "Could not find method in self.functions and no "\ 886*cda5da8dSAndroid Build Coastguard Worker "instance installed" 887*cda5da8dSAndroid Build Coastguard Worker 888*cda5da8dSAndroid Build Coastguard Worker methods[method_name] = method 889*cda5da8dSAndroid Build Coastguard Worker 890*cda5da8dSAndroid Build Coastguard Worker documenter = ServerHTMLDoc() 891*cda5da8dSAndroid Build Coastguard Worker documentation = documenter.docserver( 892*cda5da8dSAndroid Build Coastguard Worker self.server_name, 893*cda5da8dSAndroid Build Coastguard Worker self.server_documentation, 894*cda5da8dSAndroid Build Coastguard Worker methods 895*cda5da8dSAndroid Build Coastguard Worker ) 896*cda5da8dSAndroid Build Coastguard Worker 897*cda5da8dSAndroid Build Coastguard Worker return documenter.page(html.escape(self.server_title), documentation) 898*cda5da8dSAndroid Build Coastguard Worker 899*cda5da8dSAndroid Build Coastguard Workerclass DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): 900*cda5da8dSAndroid Build Coastguard Worker """XML-RPC and documentation request handler class. 901*cda5da8dSAndroid Build Coastguard Worker 902*cda5da8dSAndroid Build Coastguard Worker Handles all HTTP POST requests and attempts to decode them as 903*cda5da8dSAndroid Build Coastguard Worker XML-RPC requests. 904*cda5da8dSAndroid Build Coastguard Worker 905*cda5da8dSAndroid Build Coastguard Worker Handles all HTTP GET requests and interprets them as requests 906*cda5da8dSAndroid Build Coastguard Worker for documentation. 907*cda5da8dSAndroid Build Coastguard Worker """ 908*cda5da8dSAndroid Build Coastguard Worker 909*cda5da8dSAndroid Build Coastguard Worker def _get_css(self, url): 910*cda5da8dSAndroid Build Coastguard Worker path_here = os.path.dirname(os.path.realpath(__file__)) 911*cda5da8dSAndroid Build Coastguard Worker css_path = os.path.join(path_here, "..", "pydoc_data", "_pydoc.css") 912*cda5da8dSAndroid Build Coastguard Worker with open(css_path, mode="rb") as fp: 913*cda5da8dSAndroid Build Coastguard Worker return fp.read() 914*cda5da8dSAndroid Build Coastguard Worker 915*cda5da8dSAndroid Build Coastguard Worker def do_GET(self): 916*cda5da8dSAndroid Build Coastguard Worker """Handles the HTTP GET request. 917*cda5da8dSAndroid Build Coastguard Worker 918*cda5da8dSAndroid Build Coastguard Worker Interpret all HTTP GET requests as requests for server 919*cda5da8dSAndroid Build Coastguard Worker documentation. 920*cda5da8dSAndroid Build Coastguard Worker """ 921*cda5da8dSAndroid Build Coastguard Worker # Check that the path is legal 922*cda5da8dSAndroid Build Coastguard Worker if not self.is_rpc_path_valid(): 923*cda5da8dSAndroid Build Coastguard Worker self.report_404() 924*cda5da8dSAndroid Build Coastguard Worker return 925*cda5da8dSAndroid Build Coastguard Worker 926*cda5da8dSAndroid Build Coastguard Worker if self.path.endswith('.css'): 927*cda5da8dSAndroid Build Coastguard Worker content_type = 'text/css' 928*cda5da8dSAndroid Build Coastguard Worker response = self._get_css(self.path) 929*cda5da8dSAndroid Build Coastguard Worker else: 930*cda5da8dSAndroid Build Coastguard Worker content_type = 'text/html' 931*cda5da8dSAndroid Build Coastguard Worker response = self.server.generate_html_documentation().encode('utf-8') 932*cda5da8dSAndroid Build Coastguard Worker 933*cda5da8dSAndroid Build Coastguard Worker self.send_response(200) 934*cda5da8dSAndroid Build Coastguard Worker self.send_header('Content-Type', '%s; charset=UTF-8' % content_type) 935*cda5da8dSAndroid Build Coastguard Worker self.send_header("Content-length", str(len(response))) 936*cda5da8dSAndroid Build Coastguard Worker self.end_headers() 937*cda5da8dSAndroid Build Coastguard Worker self.wfile.write(response) 938*cda5da8dSAndroid Build Coastguard Worker 939*cda5da8dSAndroid Build Coastguard Workerclass DocXMLRPCServer( SimpleXMLRPCServer, 940*cda5da8dSAndroid Build Coastguard Worker XMLRPCDocGenerator): 941*cda5da8dSAndroid Build Coastguard Worker """XML-RPC and HTML documentation server. 942*cda5da8dSAndroid Build Coastguard Worker 943*cda5da8dSAndroid Build Coastguard Worker Adds the ability to serve server documentation to the capabilities 944*cda5da8dSAndroid Build Coastguard Worker of SimpleXMLRPCServer. 945*cda5da8dSAndroid Build Coastguard Worker """ 946*cda5da8dSAndroid Build Coastguard Worker 947*cda5da8dSAndroid Build Coastguard Worker def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler, 948*cda5da8dSAndroid Build Coastguard Worker logRequests=True, allow_none=False, encoding=None, 949*cda5da8dSAndroid Build Coastguard Worker bind_and_activate=True, use_builtin_types=False): 950*cda5da8dSAndroid Build Coastguard Worker SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, 951*cda5da8dSAndroid Build Coastguard Worker allow_none, encoding, bind_and_activate, 952*cda5da8dSAndroid Build Coastguard Worker use_builtin_types) 953*cda5da8dSAndroid Build Coastguard Worker XMLRPCDocGenerator.__init__(self) 954*cda5da8dSAndroid Build Coastguard Worker 955*cda5da8dSAndroid Build Coastguard Workerclass DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler, 956*cda5da8dSAndroid Build Coastguard Worker XMLRPCDocGenerator): 957*cda5da8dSAndroid Build Coastguard Worker """Handler for XML-RPC data and documentation requests passed through 958*cda5da8dSAndroid Build Coastguard Worker CGI""" 959*cda5da8dSAndroid Build Coastguard Worker 960*cda5da8dSAndroid Build Coastguard Worker def handle_get(self): 961*cda5da8dSAndroid Build Coastguard Worker """Handles the HTTP GET request. 962*cda5da8dSAndroid Build Coastguard Worker 963*cda5da8dSAndroid Build Coastguard Worker Interpret all HTTP GET requests as requests for server 964*cda5da8dSAndroid Build Coastguard Worker documentation. 965*cda5da8dSAndroid Build Coastguard Worker """ 966*cda5da8dSAndroid Build Coastguard Worker 967*cda5da8dSAndroid Build Coastguard Worker response = self.generate_html_documentation().encode('utf-8') 968*cda5da8dSAndroid Build Coastguard Worker 969*cda5da8dSAndroid Build Coastguard Worker print('Content-Type: text/html') 970*cda5da8dSAndroid Build Coastguard Worker print('Content-Length: %d' % len(response)) 971*cda5da8dSAndroid Build Coastguard Worker print() 972*cda5da8dSAndroid Build Coastguard Worker sys.stdout.flush() 973*cda5da8dSAndroid Build Coastguard Worker sys.stdout.buffer.write(response) 974*cda5da8dSAndroid Build Coastguard Worker sys.stdout.buffer.flush() 975*cda5da8dSAndroid Build Coastguard Worker 976*cda5da8dSAndroid Build Coastguard Worker def __init__(self): 977*cda5da8dSAndroid Build Coastguard Worker CGIXMLRPCRequestHandler.__init__(self) 978*cda5da8dSAndroid Build Coastguard Worker XMLRPCDocGenerator.__init__(self) 979*cda5da8dSAndroid Build Coastguard Worker 980*cda5da8dSAndroid Build Coastguard Worker 981*cda5da8dSAndroid Build Coastguard Workerif __name__ == '__main__': 982*cda5da8dSAndroid Build Coastguard Worker import datetime 983*cda5da8dSAndroid Build Coastguard Worker 984*cda5da8dSAndroid Build Coastguard Worker class ExampleService: 985*cda5da8dSAndroid Build Coastguard Worker def getData(self): 986*cda5da8dSAndroid Build Coastguard Worker return '42' 987*cda5da8dSAndroid Build Coastguard Worker 988*cda5da8dSAndroid Build Coastguard Worker class currentTime: 989*cda5da8dSAndroid Build Coastguard Worker @staticmethod 990*cda5da8dSAndroid Build Coastguard Worker def getCurrentTime(): 991*cda5da8dSAndroid Build Coastguard Worker return datetime.datetime.now() 992*cda5da8dSAndroid Build Coastguard Worker 993*cda5da8dSAndroid Build Coastguard Worker with SimpleXMLRPCServer(("localhost", 8000)) as server: 994*cda5da8dSAndroid Build Coastguard Worker server.register_function(pow) 995*cda5da8dSAndroid Build Coastguard Worker server.register_function(lambda x,y: x+y, 'add') 996*cda5da8dSAndroid Build Coastguard Worker server.register_instance(ExampleService(), allow_dotted_names=True) 997*cda5da8dSAndroid Build Coastguard Worker server.register_multicall_functions() 998*cda5da8dSAndroid Build Coastguard Worker print('Serving XML-RPC on localhost port 8000') 999*cda5da8dSAndroid Build Coastguard Worker print('It is advisable to run this example server within a secure, closed network.') 1000*cda5da8dSAndroid Build Coastguard Worker try: 1001*cda5da8dSAndroid Build Coastguard Worker server.serve_forever() 1002*cda5da8dSAndroid Build Coastguard Worker except KeyboardInterrupt: 1003*cda5da8dSAndroid Build Coastguard Worker print("\nKeyboard interrupt received, exiting.") 1004*cda5da8dSAndroid Build Coastguard Worker sys.exit(0) 1005