xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/xmlrpc/server.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
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