symbian-qemu-0.9.1-12/python-win32-2.6.1/lib/SimpleXMLRPCServer.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 """Simple XML-RPC Server.
       
     2 
       
     3 This module can be used to create simple XML-RPC servers
       
     4 by creating a server and either installing functions, a
       
     5 class instance, or by extending the SimpleXMLRPCServer
       
     6 class.
       
     7 
       
     8 It can also be used to handle XML-RPC requests in a CGI
       
     9 environment using CGIXMLRPCRequestHandler.
       
    10 
       
    11 A list of possible usage patterns follows:
       
    12 
       
    13 1. Install functions:
       
    14 
       
    15 server = SimpleXMLRPCServer(("localhost", 8000))
       
    16 server.register_function(pow)
       
    17 server.register_function(lambda x,y: x+y, 'add')
       
    18 server.serve_forever()
       
    19 
       
    20 2. Install an instance:
       
    21 
       
    22 class MyFuncs:
       
    23     def __init__(self):
       
    24         # make all of the string functions available through
       
    25         # string.func_name
       
    26         import string
       
    27         self.string = string
       
    28     def _listMethods(self):
       
    29         # implement this method so that system.listMethods
       
    30         # knows to advertise the strings methods
       
    31         return list_public_methods(self) + \
       
    32                 ['string.' + method for method in list_public_methods(self.string)]
       
    33     def pow(self, x, y): return pow(x, y)
       
    34     def add(self, x, y) : return x + y
       
    35 
       
    36 server = SimpleXMLRPCServer(("localhost", 8000))
       
    37 server.register_introspection_functions()
       
    38 server.register_instance(MyFuncs())
       
    39 server.serve_forever()
       
    40 
       
    41 3. Install an instance with custom dispatch method:
       
    42 
       
    43 class Math:
       
    44     def _listMethods(self):
       
    45         # this method must be present for system.listMethods
       
    46         # to work
       
    47         return ['add', 'pow']
       
    48     def _methodHelp(self, method):
       
    49         # this method must be present for system.methodHelp
       
    50         # to work
       
    51         if method == 'add':
       
    52             return "add(2,3) => 5"
       
    53         elif method == 'pow':
       
    54             return "pow(x, y[, z]) => number"
       
    55         else:
       
    56             # By convention, return empty
       
    57             # string if no help is available
       
    58             return ""
       
    59     def _dispatch(self, method, params):
       
    60         if method == 'pow':
       
    61             return pow(*params)
       
    62         elif method == 'add':
       
    63             return params[0] + params[1]
       
    64         else:
       
    65             raise 'bad method'
       
    66 
       
    67 server = SimpleXMLRPCServer(("localhost", 8000))
       
    68 server.register_introspection_functions()
       
    69 server.register_instance(Math())
       
    70 server.serve_forever()
       
    71 
       
    72 4. Subclass SimpleXMLRPCServer:
       
    73 
       
    74 class MathServer(SimpleXMLRPCServer):
       
    75     def _dispatch(self, method, params):
       
    76         try:
       
    77             # We are forcing the 'export_' prefix on methods that are
       
    78             # callable through XML-RPC to prevent potential security
       
    79             # problems
       
    80             func = getattr(self, 'export_' + method)
       
    81         except AttributeError:
       
    82             raise Exception('method "%s" is not supported' % method)
       
    83         else:
       
    84             return func(*params)
       
    85 
       
    86     def export_add(self, x, y):
       
    87         return x + y
       
    88 
       
    89 server = MathServer(("localhost", 8000))
       
    90 server.serve_forever()
       
    91 
       
    92 5. CGI script:
       
    93 
       
    94 server = CGIXMLRPCRequestHandler()
       
    95 server.register_function(pow)
       
    96 server.handle_request()
       
    97 """
       
    98 
       
    99 # Written by Brian Quinlan (brian@sweetapp.com).
       
   100 # Based on code written by Fredrik Lundh.
       
   101 
       
   102 import xmlrpclib
       
   103 from xmlrpclib import Fault
       
   104 import SocketServer
       
   105 import BaseHTTPServer
       
   106 import sys
       
   107 import os
       
   108 import traceback
       
   109 try:
       
   110     import fcntl
       
   111 except ImportError:
       
   112     fcntl = None
       
   113 
       
   114 def resolve_dotted_attribute(obj, attr, allow_dotted_names=True):
       
   115     """resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d
       
   116 
       
   117     Resolves a dotted attribute name to an object.  Raises
       
   118     an AttributeError if any attribute in the chain starts with a '_'.
       
   119 
       
   120     If the optional allow_dotted_names argument is false, dots are not
       
   121     supported and this function operates similar to getattr(obj, attr).
       
   122     """
       
   123 
       
   124     if allow_dotted_names:
       
   125         attrs = attr.split('.')
       
   126     else:
       
   127         attrs = [attr]
       
   128 
       
   129     for i in attrs:
       
   130         if i.startswith('_'):
       
   131             raise AttributeError(
       
   132                 'attempt to access private attribute "%s"' % i
       
   133                 )
       
   134         else:
       
   135             obj = getattr(obj,i)
       
   136     return obj
       
   137 
       
   138 def list_public_methods(obj):
       
   139     """Returns a list of attribute strings, found in the specified
       
   140     object, which represent callable attributes"""
       
   141 
       
   142     return [member for member in dir(obj)
       
   143                 if not member.startswith('_') and
       
   144                     hasattr(getattr(obj, member), '__call__')]
       
   145 
       
   146 def remove_duplicates(lst):
       
   147     """remove_duplicates([2,2,2,1,3,3]) => [3,1,2]
       
   148 
       
   149     Returns a copy of a list without duplicates. Every list
       
   150     item must be hashable and the order of the items in the
       
   151     resulting list is not defined.
       
   152     """
       
   153     u = {}
       
   154     for x in lst:
       
   155         u[x] = 1
       
   156 
       
   157     return u.keys()
       
   158 
       
   159 class SimpleXMLRPCDispatcher:
       
   160     """Mix-in class that dispatches XML-RPC requests.
       
   161 
       
   162     This class is used to register XML-RPC method handlers
       
   163     and then to dispatch them. There should never be any
       
   164     reason to instantiate this class directly.
       
   165     """
       
   166 
       
   167     def __init__(self, allow_none, encoding):
       
   168         self.funcs = {}
       
   169         self.instance = None
       
   170         self.allow_none = allow_none
       
   171         self.encoding = encoding
       
   172 
       
   173     def register_instance(self, instance, allow_dotted_names=False):
       
   174         """Registers an instance to respond to XML-RPC requests.
       
   175 
       
   176         Only one instance can be installed at a time.
       
   177 
       
   178         If the registered instance has a _dispatch method then that
       
   179         method will be called with the name of the XML-RPC method and
       
   180         its parameters as a tuple
       
   181         e.g. instance._dispatch('add',(2,3))
       
   182 
       
   183         If the registered instance does not have a _dispatch method
       
   184         then the instance will be searched to find a matching method
       
   185         and, if found, will be called. Methods beginning with an '_'
       
   186         are considered private and will not be called by
       
   187         SimpleXMLRPCServer.
       
   188 
       
   189         If a registered function matches a XML-RPC request, then it
       
   190         will be called instead of the registered instance.
       
   191 
       
   192         If the optional allow_dotted_names argument is true and the
       
   193         instance does not have a _dispatch method, method names
       
   194         containing dots are supported and resolved, as long as none of
       
   195         the name segments start with an '_'.
       
   196 
       
   197             *** SECURITY WARNING: ***
       
   198 
       
   199             Enabling the allow_dotted_names options allows intruders
       
   200             to access your module's global variables and may allow
       
   201             intruders to execute arbitrary code on your machine.  Only
       
   202             use this option on a secure, closed network.
       
   203 
       
   204         """
       
   205 
       
   206         self.instance = instance
       
   207         self.allow_dotted_names = allow_dotted_names
       
   208 
       
   209     def register_function(self, function, name = None):
       
   210         """Registers a function to respond to XML-RPC requests.
       
   211 
       
   212         The optional name argument can be used to set a Unicode name
       
   213         for the function.
       
   214         """
       
   215 
       
   216         if name is None:
       
   217             name = function.__name__
       
   218         self.funcs[name] = function
       
   219 
       
   220     def register_introspection_functions(self):
       
   221         """Registers the XML-RPC introspection methods in the system
       
   222         namespace.
       
   223 
       
   224         see http://xmlrpc.usefulinc.com/doc/reserved.html
       
   225         """
       
   226 
       
   227         self.funcs.update({'system.listMethods' : self.system_listMethods,
       
   228                       'system.methodSignature' : self.system_methodSignature,
       
   229                       'system.methodHelp' : self.system_methodHelp})
       
   230 
       
   231     def register_multicall_functions(self):
       
   232         """Registers the XML-RPC multicall method in the system
       
   233         namespace.
       
   234 
       
   235         see http://www.xmlrpc.com/discuss/msgReader$1208"""
       
   236 
       
   237         self.funcs.update({'system.multicall' : self.system_multicall})
       
   238 
       
   239     def _marshaled_dispatch(self, data, dispatch_method = None):
       
   240         """Dispatches an XML-RPC method from marshalled (XML) data.
       
   241 
       
   242         XML-RPC methods are dispatched from the marshalled (XML) data
       
   243         using the _dispatch method and the result is returned as
       
   244         marshalled data. For backwards compatibility, a dispatch
       
   245         function can be provided as an argument (see comment in
       
   246         SimpleXMLRPCRequestHandler.do_POST) but overriding the
       
   247         existing method through subclassing is the prefered means
       
   248         of changing method dispatch behavior.
       
   249         """
       
   250 
       
   251         try:
       
   252             params, method = xmlrpclib.loads(data)
       
   253 
       
   254             # generate response
       
   255             if dispatch_method is not None:
       
   256                 response = dispatch_method(method, params)
       
   257             else:
       
   258                 response = self._dispatch(method, params)
       
   259             # wrap response in a singleton tuple
       
   260             response = (response,)
       
   261             response = xmlrpclib.dumps(response, methodresponse=1,
       
   262                                        allow_none=self.allow_none, encoding=self.encoding)
       
   263         except Fault, fault:
       
   264             response = xmlrpclib.dumps(fault, allow_none=self.allow_none,
       
   265                                        encoding=self.encoding)
       
   266         except:
       
   267             # report exception back to server
       
   268             exc_type, exc_value, exc_tb = sys.exc_info()
       
   269             response = xmlrpclib.dumps(
       
   270                 xmlrpclib.Fault(1, "%s:%s" % (exc_type, exc_value)),
       
   271                 encoding=self.encoding, allow_none=self.allow_none,
       
   272                 )
       
   273 
       
   274         return response
       
   275 
       
   276     def system_listMethods(self):
       
   277         """system.listMethods() => ['add', 'subtract', 'multiple']
       
   278 
       
   279         Returns a list of the methods supported by the server."""
       
   280 
       
   281         methods = self.funcs.keys()
       
   282         if self.instance is not None:
       
   283             # Instance can implement _listMethod to return a list of
       
   284             # methods
       
   285             if hasattr(self.instance, '_listMethods'):
       
   286                 methods = remove_duplicates(
       
   287                         methods + self.instance._listMethods()
       
   288                     )
       
   289             # if the instance has a _dispatch method then we
       
   290             # don't have enough information to provide a list
       
   291             # of methods
       
   292             elif not hasattr(self.instance, '_dispatch'):
       
   293                 methods = remove_duplicates(
       
   294                         methods + list_public_methods(self.instance)
       
   295                     )
       
   296         methods.sort()
       
   297         return methods
       
   298 
       
   299     def system_methodSignature(self, method_name):
       
   300         """system.methodSignature('add') => [double, int, int]
       
   301 
       
   302         Returns a list describing the signature of the method. In the
       
   303         above example, the add method takes two integers as arguments
       
   304         and returns a double result.
       
   305 
       
   306         This server does NOT support system.methodSignature."""
       
   307 
       
   308         # See http://xmlrpc.usefulinc.com/doc/sysmethodsig.html
       
   309 
       
   310         return 'signatures not supported'
       
   311 
       
   312     def system_methodHelp(self, method_name):
       
   313         """system.methodHelp('add') => "Adds two integers together"
       
   314 
       
   315         Returns a string containing documentation for the specified method."""
       
   316 
       
   317         method = None
       
   318         if method_name in self.funcs:
       
   319             method = self.funcs[method_name]
       
   320         elif self.instance is not None:
       
   321             # Instance can implement _methodHelp to return help for a method
       
   322             if hasattr(self.instance, '_methodHelp'):
       
   323                 return self.instance._methodHelp(method_name)
       
   324             # if the instance has a _dispatch method then we
       
   325             # don't have enough information to provide help
       
   326             elif not hasattr(self.instance, '_dispatch'):
       
   327                 try:
       
   328                     method = resolve_dotted_attribute(
       
   329                                 self.instance,
       
   330                                 method_name,
       
   331                                 self.allow_dotted_names
       
   332                                 )
       
   333                 except AttributeError:
       
   334                     pass
       
   335 
       
   336         # Note that we aren't checking that the method actually
       
   337         # be a callable object of some kind
       
   338         if method is None:
       
   339             return ""
       
   340         else:
       
   341             import pydoc
       
   342             return pydoc.getdoc(method)
       
   343 
       
   344     def system_multicall(self, call_list):
       
   345         """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \
       
   346 [[4], ...]
       
   347 
       
   348         Allows the caller to package multiple XML-RPC calls into a single
       
   349         request.
       
   350 
       
   351         See http://www.xmlrpc.com/discuss/msgReader$1208
       
   352         """
       
   353 
       
   354         results = []
       
   355         for call in call_list:
       
   356             method_name = call['methodName']
       
   357             params = call['params']
       
   358 
       
   359             try:
       
   360                 # XXX A marshalling error in any response will fail the entire
       
   361                 # multicall. If someone cares they should fix this.
       
   362                 results.append([self._dispatch(method_name, params)])
       
   363             except Fault, fault:
       
   364                 results.append(
       
   365                     {'faultCode' : fault.faultCode,
       
   366                      'faultString' : fault.faultString}
       
   367                     )
       
   368             except:
       
   369                 exc_type, exc_value, exc_tb = sys.exc_info()
       
   370                 results.append(
       
   371                     {'faultCode' : 1,
       
   372                      'faultString' : "%s:%s" % (exc_type, exc_value)}
       
   373                     )
       
   374         return results
       
   375 
       
   376     def _dispatch(self, method, params):
       
   377         """Dispatches the XML-RPC method.
       
   378 
       
   379         XML-RPC calls are forwarded to a registered function that
       
   380         matches the called XML-RPC method name. If no such function
       
   381         exists then the call is forwarded to the registered instance,
       
   382         if available.
       
   383 
       
   384         If the registered instance has a _dispatch method then that
       
   385         method will be called with the name of the XML-RPC method and
       
   386         its parameters as a tuple
       
   387         e.g. instance._dispatch('add',(2,3))
       
   388 
       
   389         If the registered instance does not have a _dispatch method
       
   390         then the instance will be searched to find a matching method
       
   391         and, if found, will be called.
       
   392 
       
   393         Methods beginning with an '_' are considered private and will
       
   394         not be called.
       
   395         """
       
   396 
       
   397         func = None
       
   398         try:
       
   399             # check to see if a matching function has been registered
       
   400             func = self.funcs[method]
       
   401         except KeyError:
       
   402             if self.instance is not None:
       
   403                 # check for a _dispatch method
       
   404                 if hasattr(self.instance, '_dispatch'):
       
   405                     return self.instance._dispatch(method, params)
       
   406                 else:
       
   407                     # call instance method directly
       
   408                     try:
       
   409                         func = resolve_dotted_attribute(
       
   410                             self.instance,
       
   411                             method,
       
   412                             self.allow_dotted_names
       
   413                             )
       
   414                     except AttributeError:
       
   415                         pass
       
   416 
       
   417         if func is not None:
       
   418             return func(*params)
       
   419         else:
       
   420             raise Exception('method "%s" is not supported' % method)
       
   421 
       
   422 class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
       
   423     """Simple XML-RPC request handler class.
       
   424 
       
   425     Handles all HTTP POST requests and attempts to decode them as
       
   426     XML-RPC requests.
       
   427     """
       
   428 
       
   429     # Class attribute listing the accessible path components;
       
   430     # paths not on this list will result in a 404 error.
       
   431     rpc_paths = ('/', '/RPC2')
       
   432 
       
   433     def is_rpc_path_valid(self):
       
   434         if self.rpc_paths:
       
   435             return self.path in self.rpc_paths
       
   436         else:
       
   437             # If .rpc_paths is empty, just assume all paths are legal
       
   438             return True
       
   439 
       
   440     def do_POST(self):
       
   441         """Handles the HTTP POST request.
       
   442 
       
   443         Attempts to interpret all HTTP POST requests as XML-RPC calls,
       
   444         which are forwarded to the server's _dispatch method for handling.
       
   445         """
       
   446 
       
   447         # Check that the path is legal
       
   448         if not self.is_rpc_path_valid():
       
   449             self.report_404()
       
   450             return
       
   451 
       
   452         try:
       
   453             # Get arguments by reading body of request.
       
   454             # We read this in chunks to avoid straining
       
   455             # socket.read(); around the 10 or 15Mb mark, some platforms
       
   456             # begin to have problems (bug #792570).
       
   457             max_chunk_size = 10*1024*1024
       
   458             size_remaining = int(self.headers["content-length"])
       
   459             L = []
       
   460             while size_remaining:
       
   461                 chunk_size = min(size_remaining, max_chunk_size)
       
   462                 L.append(self.rfile.read(chunk_size))
       
   463                 size_remaining -= len(L[-1])
       
   464             data = ''.join(L)
       
   465 
       
   466             # In previous versions of SimpleXMLRPCServer, _dispatch
       
   467             # could be overridden in this class, instead of in
       
   468             # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
       
   469             # check to see if a subclass implements _dispatch and dispatch
       
   470             # using that method if present.
       
   471             response = self.server._marshaled_dispatch(
       
   472                     data, getattr(self, '_dispatch', None)
       
   473                 )
       
   474         except Exception, e: # This should only happen if the module is buggy
       
   475             # internal error, report as HTTP server error
       
   476             self.send_response(500)
       
   477 
       
   478             # Send information about the exception if requested
       
   479             if hasattr(self.server, '_send_traceback_header') and \
       
   480                     self.server._send_traceback_header:
       
   481                 self.send_header("X-exception", str(e))
       
   482                 self.send_header("X-traceback", traceback.format_exc())
       
   483 
       
   484             self.end_headers()
       
   485         else:
       
   486             # got a valid XML RPC response
       
   487             self.send_response(200)
       
   488             self.send_header("Content-type", "text/xml")
       
   489             self.send_header("Content-length", str(len(response)))
       
   490             self.end_headers()
       
   491             self.wfile.write(response)
       
   492 
       
   493             # shut down the connection
       
   494             self.wfile.flush()
       
   495             self.connection.shutdown(1)
       
   496 
       
   497     def report_404 (self):
       
   498             # Report a 404 error
       
   499         self.send_response(404)
       
   500         response = 'No such page'
       
   501         self.send_header("Content-type", "text/plain")
       
   502         self.send_header("Content-length", str(len(response)))
       
   503         self.end_headers()
       
   504         self.wfile.write(response)
       
   505         # shut down the connection
       
   506         self.wfile.flush()
       
   507         self.connection.shutdown(1)
       
   508 
       
   509     def log_request(self, code='-', size='-'):
       
   510         """Selectively log an accepted request."""
       
   511 
       
   512         if self.server.logRequests:
       
   513             BaseHTTPServer.BaseHTTPRequestHandler.log_request(self, code, size)
       
   514 
       
   515 class SimpleXMLRPCServer(SocketServer.TCPServer,
       
   516                          SimpleXMLRPCDispatcher):
       
   517     """Simple XML-RPC server.
       
   518 
       
   519     Simple XML-RPC server that allows functions and a single instance
       
   520     to be installed to handle requests. The default implementation
       
   521     attempts to dispatch XML-RPC calls to the functions or instance
       
   522     installed in the server. Override the _dispatch method inhereted
       
   523     from SimpleXMLRPCDispatcher to change this behavior.
       
   524     """
       
   525 
       
   526     allow_reuse_address = True
       
   527 
       
   528     # Warning: this is for debugging purposes only! Never set this to True in
       
   529     # production code, as will be sending out sensitive information (exception
       
   530     # and stack trace details) when exceptions are raised inside
       
   531     # SimpleXMLRPCRequestHandler.do_POST
       
   532     _send_traceback_header = False
       
   533 
       
   534     def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
       
   535                  logRequests=True, allow_none=False, encoding=None, bind_and_activate=True):
       
   536         self.logRequests = logRequests
       
   537 
       
   538         SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
       
   539         SocketServer.TCPServer.__init__(self, addr, requestHandler, bind_and_activate)
       
   540 
       
   541         # [Bug #1222790] If possible, set close-on-exec flag; if a
       
   542         # method spawns a subprocess, the subprocess shouldn't have
       
   543         # the listening socket open.
       
   544         if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
       
   545             flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
       
   546             flags |= fcntl.FD_CLOEXEC
       
   547             fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
       
   548 
       
   549 class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher):
       
   550     """Simple handler for XML-RPC data passed through CGI."""
       
   551 
       
   552     def __init__(self, allow_none=False, encoding=None):
       
   553         SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
       
   554 
       
   555     def handle_xmlrpc(self, request_text):
       
   556         """Handle a single XML-RPC request"""
       
   557 
       
   558         response = self._marshaled_dispatch(request_text)
       
   559 
       
   560         print 'Content-Type: text/xml'
       
   561         print 'Content-Length: %d' % len(response)
       
   562         print
       
   563         sys.stdout.write(response)
       
   564 
       
   565     def handle_get(self):
       
   566         """Handle a single HTTP GET request.
       
   567 
       
   568         Default implementation indicates an error because
       
   569         XML-RPC uses the POST method.
       
   570         """
       
   571 
       
   572         code = 400
       
   573         message, explain = \
       
   574                  BaseHTTPServer.BaseHTTPRequestHandler.responses[code]
       
   575 
       
   576         response = BaseHTTPServer.DEFAULT_ERROR_MESSAGE % \
       
   577             {
       
   578              'code' : code,
       
   579              'message' : message,
       
   580              'explain' : explain
       
   581             }
       
   582         print 'Status: %d %s' % (code, message)
       
   583         print 'Content-Type: text/html'
       
   584         print 'Content-Length: %d' % len(response)
       
   585         print
       
   586         sys.stdout.write(response)
       
   587 
       
   588     def handle_request(self, request_text = None):
       
   589         """Handle a single XML-RPC request passed through a CGI post method.
       
   590 
       
   591         If no XML data is given then it is read from stdin. The resulting
       
   592         XML-RPC response is printed to stdout along with the correct HTTP
       
   593         headers.
       
   594         """
       
   595 
       
   596         if request_text is None and \
       
   597             os.environ.get('REQUEST_METHOD', None) == 'GET':
       
   598             self.handle_get()
       
   599         else:
       
   600             # POST data is normally available through stdin
       
   601             if request_text is None:
       
   602                 request_text = sys.stdin.read()
       
   603 
       
   604             self.handle_xmlrpc(request_text)
       
   605 
       
   606 if __name__ == '__main__':
       
   607     print 'Running XML-RPC server on port 8000'
       
   608     server = SimpleXMLRPCServer(("localhost", 8000))
       
   609     server.register_function(pow)
       
   610     server.register_function(lambda x,y: x+y, 'add')
       
   611     server.serve_forever()