Home | History | Annotate | Download | only in python2.7
      1 r"""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 (at] 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 import re
    110 try:
    111     import fcntl
    112 except ImportError:
    113     fcntl = None
    114 
    115 def resolve_dotted_attribute(obj, attr, allow_dotted_names=True):
    116     """resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d
    117 
    118     Resolves a dotted attribute name to an object.  Raises
    119     an AttributeError if any attribute in the chain starts with a '_'.
    120 
    121     If the optional allow_dotted_names argument is false, dots are not
    122     supported and this function operates similar to getattr(obj, attr).
    123     """
    124 
    125     if allow_dotted_names:
    126         attrs = attr.split('.')
    127     else:
    128         attrs = [attr]
    129 
    130     for i in attrs:
    131         if i.startswith('_'):
    132             raise AttributeError(
    133                 'attempt to access private attribute "%s"' % i
    134                 )
    135         else:
    136             obj = getattr(obj,i)
    137     return obj
    138 
    139 def list_public_methods(obj):
    140     """Returns a list of attribute strings, found in the specified
    141     object, which represent callable attributes"""
    142 
    143     return [member for member in dir(obj)
    144                 if not member.startswith('_') and
    145                     hasattr(getattr(obj, member), '__call__')]
    146 
    147 def remove_duplicates(lst):
    148     """remove_duplicates([2,2,2,1,3,3]) => [3,1,2]
    149 
    150     Returns a copy of a list without duplicates. Every list
    151     item must be hashable and the order of the items in the
    152     resulting list is not defined.
    153     """
    154     u = {}
    155     for x in lst:
    156         u[x] = 1
    157 
    158     return u.keys()
    159 
    160 class SimpleXMLRPCDispatcher:
    161     """Mix-in class that dispatches XML-RPC requests.
    162 
    163     This class is used to register XML-RPC method handlers
    164     and then to dispatch them. This class doesn't need to be
    165     instanced directly when used by SimpleXMLRPCServer but it
    166     can be instanced when used by the MultiPathXMLRPCServer.
    167     """
    168 
    169     def __init__(self, allow_none=False, encoding=None):
    170         self.funcs = {}
    171         self.instance = None
    172         self.allow_none = allow_none
    173         self.encoding = encoding
    174 
    175     def register_instance(self, instance, allow_dotted_names=False):
    176         """Registers an instance to respond to XML-RPC requests.
    177 
    178         Only one instance can be installed at a time.
    179 
    180         If the registered instance has a _dispatch method then that
    181         method will be called with the name of the XML-RPC method and
    182         its parameters as a tuple
    183         e.g. instance._dispatch('add',(2,3))
    184 
    185         If the registered instance does not have a _dispatch method
    186         then the instance will be searched to find a matching method
    187         and, if found, will be called. Methods beginning with an '_'
    188         are considered private and will not be called by
    189         SimpleXMLRPCServer.
    190 
    191         If a registered function matches a XML-RPC request, then it
    192         will be called instead of the registered instance.
    193 
    194         If the optional allow_dotted_names argument is true and the
    195         instance does not have a _dispatch method, method names
    196         containing dots are supported and resolved, as long as none of
    197         the name segments start with an '_'.
    198 
    199             *** SECURITY WARNING: ***
    200 
    201             Enabling the allow_dotted_names options allows intruders
    202             to access your module's global variables and may allow
    203             intruders to execute arbitrary code on your machine.  Only
    204             use this option on a secure, closed network.
    205 
    206         """
    207 
    208         self.instance = instance
    209         self.allow_dotted_names = allow_dotted_names
    210 
    211     def register_function(self, function, name = None):
    212         """Registers a function to respond to XML-RPC requests.
    213 
    214         The optional name argument can be used to set a Unicode name
    215         for the function.
    216         """
    217 
    218         if name is None:
    219             name = function.__name__
    220         self.funcs[name] = function
    221 
    222     def register_introspection_functions(self):
    223         """Registers the XML-RPC introspection methods in the system
    224         namespace.
    225 
    226         see http://xmlrpc.usefulinc.com/doc/reserved.html
    227         """
    228 
    229         self.funcs.update({'system.listMethods' : self.system_listMethods,
    230                       'system.methodSignature' : self.system_methodSignature,
    231                       'system.methodHelp' : self.system_methodHelp})
    232 
    233     def register_multicall_functions(self):
    234         """Registers the XML-RPC multicall method in the system
    235         namespace.
    236 
    237         see http://www.xmlrpc.com/discuss/msgReader$1208"""
    238 
    239         self.funcs.update({'system.multicall' : self.system_multicall})
    240 
    241     def _marshaled_dispatch(self, data, dispatch_method = None, path = None):
    242         """Dispatches an XML-RPC method from marshalled (XML) data.
    243 
    244         XML-RPC methods are dispatched from the marshalled (XML) data
    245         using the _dispatch method and the result is returned as
    246         marshalled data. For backwards compatibility, a dispatch
    247         function can be provided as an argument (see comment in
    248         SimpleXMLRPCRequestHandler.do_POST) but overriding the
    249         existing method through subclassing is the preferred means
    250         of changing method dispatch behavior.
    251         """
    252 
    253         try:
    254             params, method = xmlrpclib.loads(data)
    255 
    256             # generate response
    257             if dispatch_method is not None:
    258                 response = dispatch_method(method, params)
    259             else:
    260                 response = self._dispatch(method, params)
    261             # wrap response in a singleton tuple
    262             response = (response,)
    263             response = xmlrpclib.dumps(response, methodresponse=1,
    264                                        allow_none=self.allow_none, encoding=self.encoding)
    265         except Fault, fault:
    266             response = xmlrpclib.dumps(fault, allow_none=self.allow_none,
    267                                        encoding=self.encoding)
    268         except:
    269             # report exception back to server
    270             exc_type, exc_value, exc_tb = sys.exc_info()
    271             response = xmlrpclib.dumps(
    272                 xmlrpclib.Fault(1, "%s:%s" % (exc_type, exc_value)),
    273                 encoding=self.encoding, allow_none=self.allow_none,
    274                 )
    275 
    276         return response
    277 
    278     def system_listMethods(self):
    279         """system.listMethods() => ['add', 'subtract', 'multiple']
    280 
    281         Returns a list of the methods supported by the server."""
    282 
    283         methods = self.funcs.keys()
    284         if self.instance is not None:
    285             # Instance can implement _listMethod to return a list of
    286             # methods
    287             if hasattr(self.instance, '_listMethods'):
    288                 methods = remove_duplicates(
    289                         methods + self.instance._listMethods()
    290                     )
    291             # if the instance has a _dispatch method then we
    292             # don't have enough information to provide a list
    293             # of methods
    294             elif not hasattr(self.instance, '_dispatch'):
    295                 methods = remove_duplicates(
    296                         methods + list_public_methods(self.instance)
    297                     )
    298         methods.sort()
    299         return methods
    300 
    301     def system_methodSignature(self, method_name):
    302         """system.methodSignature('add') => [double, int, int]
    303 
    304         Returns a list describing the signature of the method. In the
    305         above example, the add method takes two integers as arguments
    306         and returns a double result.
    307 
    308         This server does NOT support system.methodSignature."""
    309 
    310         # See http://xmlrpc.usefulinc.com/doc/sysmethodsig.html
    311 
    312         return 'signatures not supported'
    313 
    314     def system_methodHelp(self, method_name):
    315         """system.methodHelp('add') => "Adds two integers together"
    316 
    317         Returns a string containing documentation for the specified method."""
    318 
    319         method = None
    320         if method_name in self.funcs:
    321             method = self.funcs[method_name]
    322         elif self.instance is not None:
    323             # Instance can implement _methodHelp to return help for a method
    324             if hasattr(self.instance, '_methodHelp'):
    325                 return self.instance._methodHelp(method_name)
    326             # if the instance has a _dispatch method then we
    327             # don't have enough information to provide help
    328             elif not hasattr(self.instance, '_dispatch'):
    329                 try:
    330                     method = resolve_dotted_attribute(
    331                                 self.instance,
    332                                 method_name,
    333                                 self.allow_dotted_names
    334                                 )
    335                 except AttributeError:
    336                     pass
    337 
    338         # Note that we aren't checking that the method actually
    339         # be a callable object of some kind
    340         if method is None:
    341             return ""
    342         else:
    343             import pydoc
    344             return pydoc.getdoc(method)
    345 
    346     def system_multicall(self, call_list):
    347         """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \
    348 [[4], ...]
    349 
    350         Allows the caller to package multiple XML-RPC calls into a single
    351         request.
    352 
    353         See http://www.xmlrpc.com/discuss/msgReader$1208
    354         """
    355 
    356         results = []
    357         for call in call_list:
    358             method_name = call['methodName']
    359             params = call['params']
    360 
    361             try:
    362                 # XXX A marshalling error in any response will fail the entire
    363                 # multicall. If someone cares they should fix this.
    364                 results.append([self._dispatch(method_name, params)])
    365             except Fault, fault:
    366                 results.append(
    367                     {'faultCode' : fault.faultCode,
    368                      'faultString' : fault.faultString}
    369                     )
    370             except:
    371                 exc_type, exc_value, exc_tb = sys.exc_info()
    372                 results.append(
    373                     {'faultCode' : 1,
    374                      'faultString' : "%s:%s" % (exc_type, exc_value)}
    375                     )
    376         return results
    377 
    378     def _dispatch(self, method, params):
    379         """Dispatches the XML-RPC method.
    380 
    381         XML-RPC calls are forwarded to a registered function that
    382         matches the called XML-RPC method name. If no such function
    383         exists then the call is forwarded to the registered instance,
    384         if available.
    385 
    386         If the registered instance has a _dispatch method then that
    387         method will be called with the name of the XML-RPC method and
    388         its parameters as a tuple
    389         e.g. instance._dispatch('add',(2,3))
    390 
    391         If the registered instance does not have a _dispatch method
    392         then the instance will be searched to find a matching method
    393         and, if found, will be called.
    394 
    395         Methods beginning with an '_' are considered private and will
    396         not be called.
    397         """
    398 
    399         func = None
    400         try:
    401             # check to see if a matching function has been registered
    402             func = self.funcs[method]
    403         except KeyError:
    404             if self.instance is not None:
    405                 # check for a _dispatch method
    406                 if hasattr(self.instance, '_dispatch'):
    407                     return self.instance._dispatch(method, params)
    408                 else:
    409                     # call instance method directly
    410                     try:
    411                         func = resolve_dotted_attribute(
    412                             self.instance,
    413                             method,
    414                             self.allow_dotted_names
    415                             )
    416                     except AttributeError:
    417                         pass
    418 
    419         if func is not None:
    420             return func(*params)
    421         else:
    422             raise Exception('method "%s" is not supported' % method)
    423 
    424 class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    425     """Simple XML-RPC request handler class.
    426 
    427     Handles all HTTP POST requests and attempts to decode them as
    428     XML-RPC requests.
    429     """
    430 
    431     # Class attribute listing the accessible path components;
    432     # paths not on this list will result in a 404 error.
    433     rpc_paths = ('/', '/RPC2')
    434 
    435     #if not None, encode responses larger than this, if possible
    436     encode_threshold = 1400 #a common MTU
    437 
    438     #Override form StreamRequestHandler: full buffering of output
    439     #and no Nagle.
    440     wbufsize = -1
    441     disable_nagle_algorithm = True
    442 
    443     # a re to match a gzip Accept-Encoding
    444     aepattern = re.compile(r"""
    445                             \s* ([^\s;]+) \s*            #content-coding
    446                             (;\s* q \s*=\s* ([0-9\.]+))? #q
    447                             """, re.VERBOSE | re.IGNORECASE)
    448 
    449     def accept_encodings(self):
    450         r = {}
    451         ae = self.headers.get("Accept-Encoding", "")
    452         for e in ae.split(","):
    453             match = self.aepattern.match(e)
    454             if match:
    455                 v = match.group(3)
    456                 v = float(v) if v else 1.0
    457                 r[match.group(1)] = v
    458         return r
    459 
    460     def is_rpc_path_valid(self):
    461         if self.rpc_paths:
    462             return self.path in self.rpc_paths
    463         else:
    464             # If .rpc_paths is empty, just assume all paths are legal
    465             return True
    466 
    467     def do_POST(self):
    468         """Handles the HTTP POST request.
    469 
    470         Attempts to interpret all HTTP POST requests as XML-RPC calls,
    471         which are forwarded to the server's _dispatch method for handling.
    472         """
    473 
    474         # Check that the path is legal
    475         if not self.is_rpc_path_valid():
    476             self.report_404()
    477             return
    478 
    479         try:
    480             # Get arguments by reading body of request.
    481             # We read this in chunks to avoid straining
    482             # socket.read(); around the 10 or 15Mb mark, some platforms
    483             # begin to have problems (bug #792570).
    484             max_chunk_size = 10*1024*1024
    485             size_remaining = int(self.headers["content-length"])
    486             L = []
    487             while size_remaining:
    488                 chunk_size = min(size_remaining, max_chunk_size)
    489                 chunk = self.rfile.read(chunk_size)
    490                 if not chunk:
    491                     break
    492                 L.append(chunk)
    493                 size_remaining -= len(L[-1])
    494             data = ''.join(L)
    495 
    496             data = self.decode_request_content(data)
    497             if data is None:
    498                 return #response has been sent
    499 
    500             # In previous versions of SimpleXMLRPCServer, _dispatch
    501             # could be overridden in this class, instead of in
    502             # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
    503             # check to see if a subclass implements _dispatch and dispatch
    504             # using that method if present.
    505             response = self.server._marshaled_dispatch(
    506                     data, getattr(self, '_dispatch', None), self.path
    507                 )
    508         except Exception, e: # This should only happen if the module is buggy
    509             # internal error, report as HTTP server error
    510             self.send_response(500)
    511 
    512             # Send information about the exception if requested
    513             if hasattr(self.server, '_send_traceback_header') and \
    514                     self.server._send_traceback_header:
    515                 self.send_header("X-exception", str(e))
    516                 self.send_header("X-traceback", traceback.format_exc())
    517 
    518             self.send_header("Content-length", "0")
    519             self.end_headers()
    520         else:
    521             # got a valid XML RPC response
    522             self.send_response(200)
    523             self.send_header("Content-type", "text/xml")
    524             if self.encode_threshold is not None:
    525                 if len(response) > self.encode_threshold:
    526                     q = self.accept_encodings().get("gzip", 0)
    527                     if q:
    528                         try:
    529                             response = xmlrpclib.gzip_encode(response)
    530                             self.send_header("Content-Encoding", "gzip")
    531                         except NotImplementedError:
    532                             pass
    533             self.send_header("Content-length", str(len(response)))
    534             self.end_headers()
    535             self.wfile.write(response)
    536 
    537     def decode_request_content(self, data):
    538         #support gzip encoding of request
    539         encoding = self.headers.get("content-encoding", "identity").lower()
    540         if encoding == "identity":
    541             return data
    542         if encoding == "gzip":
    543             try:
    544                 return xmlrpclib.gzip_decode(data)
    545             except NotImplementedError:
    546                 self.send_response(501, "encoding %r not supported" % encoding)
    547             except ValueError:
    548                 self.send_response(400, "error decoding gzip content")
    549         else:
    550             self.send_response(501, "encoding %r not supported" % encoding)
    551         self.send_header("Content-length", "0")
    552         self.end_headers()
    553 
    554     def report_404 (self):
    555             # Report a 404 error
    556         self.send_response(404)
    557         response = 'No such page'
    558         self.send_header("Content-type", "text/plain")
    559         self.send_header("Content-length", str(len(response)))
    560         self.end_headers()
    561         self.wfile.write(response)
    562 
    563     def log_request(self, code='-', size='-'):
    564         """Selectively log an accepted request."""
    565 
    566         if self.server.logRequests:
    567             BaseHTTPServer.BaseHTTPRequestHandler.log_request(self, code, size)
    568 
    569 class SimpleXMLRPCServer(SocketServer.TCPServer,
    570                          SimpleXMLRPCDispatcher):
    571     """Simple XML-RPC server.
    572 
    573     Simple XML-RPC server that allows functions and a single instance
    574     to be installed to handle requests. The default implementation
    575     attempts to dispatch XML-RPC calls to the functions or instance
    576     installed in the server. Override the _dispatch method inhereted
    577     from SimpleXMLRPCDispatcher to change this behavior.
    578     """
    579 
    580     allow_reuse_address = True
    581 
    582     # Warning: this is for debugging purposes only! Never set this to True in
    583     # production code, as will be sending out sensitive information (exception
    584     # and stack trace details) when exceptions are raised inside
    585     # SimpleXMLRPCRequestHandler.do_POST
    586     _send_traceback_header = False
    587 
    588     def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
    589                  logRequests=True, allow_none=False, encoding=None, bind_and_activate=True):
    590         self.logRequests = logRequests
    591 
    592         SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
    593         SocketServer.TCPServer.__init__(self, addr, requestHandler, bind_and_activate)
    594 
    595         # [Bug #1222790] If possible, set close-on-exec flag; if a
    596         # method spawns a subprocess, the subprocess shouldn't have
    597         # the listening socket open.
    598         if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
    599             flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
    600             flags |= fcntl.FD_CLOEXEC
    601             fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
    602 
    603 class MultiPathXMLRPCServer(SimpleXMLRPCServer):
    604     """Multipath XML-RPC Server
    605     This specialization of SimpleXMLRPCServer allows the user to create
    606     multiple Dispatcher instances and assign them to different
    607     HTTP request paths.  This makes it possible to run two or more
    608     'virtual XML-RPC servers' at the same port.
    609     Make sure that the requestHandler accepts the paths in question.
    610     """
    611     def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
    612                  logRequests=True, allow_none=False, encoding=None, bind_and_activate=True):
    613 
    614         SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, allow_none,
    615                                     encoding, bind_and_activate)
    616         self.dispatchers = {}
    617         self.allow_none = allow_none
    618         self.encoding = encoding
    619 
    620     def add_dispatcher(self, path, dispatcher):
    621         self.dispatchers[path] = dispatcher
    622         return dispatcher
    623 
    624     def get_dispatcher(self, path):
    625         return self.dispatchers[path]
    626 
    627     def _marshaled_dispatch(self, data, dispatch_method = None, path = None):
    628         try:
    629             response = self.dispatchers[path]._marshaled_dispatch(
    630                data, dispatch_method, path)
    631         except:
    632             # report low level exception back to server
    633             # (each dispatcher should have handled their own
    634             # exceptions)
    635             exc_type, exc_value = sys.exc_info()[:2]
    636             response = xmlrpclib.dumps(
    637                 xmlrpclib.Fault(1, "%s:%s" % (exc_type, exc_value)),
    638                 encoding=self.encoding, allow_none=self.allow_none)
    639         return response
    640 
    641 class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher):
    642     """Simple handler for XML-RPC data passed through CGI."""
    643 
    644     def __init__(self, allow_none=False, encoding=None):
    645         SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
    646 
    647     def handle_xmlrpc(self, request_text):
    648         """Handle a single XML-RPC request"""
    649 
    650         response = self._marshaled_dispatch(request_text)
    651 
    652         print 'Content-Type: text/xml'
    653         print 'Content-Length: %d' % len(response)
    654         print
    655         sys.stdout.write(response)
    656 
    657     def handle_get(self):
    658         """Handle a single HTTP GET request.
    659 
    660         Default implementation indicates an error because
    661         XML-RPC uses the POST method.
    662         """
    663 
    664         code = 400
    665         message, explain = \
    666                  BaseHTTPServer.BaseHTTPRequestHandler.responses[code]
    667 
    668         response = BaseHTTPServer.DEFAULT_ERROR_MESSAGE % \
    669             {
    670              'code' : code,
    671              'message' : message,
    672              'explain' : explain
    673             }
    674         print 'Status: %d %s' % (code, message)
    675         print 'Content-Type: %s' % BaseHTTPServer.DEFAULT_ERROR_CONTENT_TYPE
    676         print 'Content-Length: %d' % len(response)
    677         print
    678         sys.stdout.write(response)
    679 
    680     def handle_request(self, request_text = None):
    681         """Handle a single XML-RPC request passed through a CGI post method.
    682 
    683         If no XML data is given then it is read from stdin. The resulting
    684         XML-RPC response is printed to stdout along with the correct HTTP
    685         headers.
    686         """
    687 
    688         if request_text is None and \
    689             os.environ.get('REQUEST_METHOD', None) == 'GET':
    690             self.handle_get()
    691         else:
    692             # POST data is normally available through stdin
    693             try:
    694                 length = int(os.environ.get('CONTENT_LENGTH', None))
    695             except (TypeError, ValueError):
    696                 length = -1
    697             if request_text is None:
    698                 request_text = sys.stdin.read(length)
    699 
    700             self.handle_xmlrpc(request_text)
    701 
    702 if __name__ == '__main__':
    703     print 'Running XML-RPC server on port 8000'
    704     server = SimpleXMLRPCServer(("localhost", 8000))
    705     server.register_function(pow)
    706     server.register_function(lambda x,y: x+y, 'add')
    707     server.serve_forever()
    708