SimpleXMLRPCServer.py (12356B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\stdlib\SimpleXMLRPCServer.py 2 import xmlrpclib 3 from xmlrpclib import Fault 4 import SocketServer 5 import BaseHTTPServer 6 import sys 7 import os 8 import traceback 9 import re 10 try: 11 import fcntl 12 except ImportError: 13 fcntl = None 14 15 def resolve_dotted_attribute(obj, attr, allow_dotted_names = True): 16 if allow_dotted_names: 17 attrs = attr.split('.') 18 else: 19 attrs = [attr] 20 for i in attrs: 21 if i.startswith('_'): 22 raise AttributeError('attempt to access private attribute "%s"' % i) 23 else: 24 obj = getattr(obj, i) 25 26 return obj 27 28 29 def list_public_methods(obj): 30 return [ member for member in dir(obj) if not member.startswith('_') and hasattr(getattr(obj, member), '__call__') ] 31 32 33 def remove_duplicates(lst): 34 u = {} 35 for x in lst: 36 u[x] = 1 37 38 return u.keys() 39 40 41 class SimpleXMLRPCDispatcher(): 42 43 def __init__(self, allow_none = False, encoding = None): 44 self.funcs = {} 45 self.instance = None 46 self.allow_none = allow_none 47 self.encoding = encoding 48 49 def register_instance(self, instance, allow_dotted_names = False): 50 self.instance = instance 51 self.allow_dotted_names = allow_dotted_names 52 53 def register_function(self, function, name = None): 54 if name is None: 55 name = function.__name__ 56 self.funcs[name] = function 57 58 def register_introspection_functions(self): 59 self.funcs.update({'system.listMethods': self.system_listMethods, 60 'system.methodSignature': self.system_methodSignature, 61 'system.methodHelp': self.system_methodHelp}) 62 63 def register_multicall_functions(self): 64 self.funcs.update({'system.multicall': self.system_multicall}) 65 66 def _marshaled_dispatch(self, data, dispatch_method = None, path = None): 67 try: 68 params, method = xmlrpclib.loads(data) 69 if dispatch_method is not None: 70 response = dispatch_method(method, params) 71 else: 72 response = self._dispatch(method, params) 73 response = (response,) 74 response = xmlrpclib.dumps(response, methodresponse=1, allow_none=self.allow_none, encoding=self.encoding) 75 except Fault as fault: 76 response = xmlrpclib.dumps(fault, allow_none=self.allow_none, encoding=self.encoding) 77 except: 78 exc_type, exc_value = sys.exc_info()[:2] 79 response = xmlrpclib.dumps(xmlrpclib.Fault(1, '%s:%s' % (exc_type, exc_value)), encoding=self.encoding, allow_none=self.allow_none) 80 81 return response 82 83 def system_listMethods(self): 84 methods = self.funcs.keys() 85 if self.instance is not None: 86 if hasattr(self.instance, '_listMethods'): 87 methods = remove_duplicates(methods + self.instance._listMethods()) 88 elif not hasattr(self.instance, '_dispatch'): 89 methods = remove_duplicates(methods + list_public_methods(self.instance)) 90 methods.sort() 91 return methods 92 93 def system_methodSignature(self, method_name): 94 return 'signatures not supported' 95 96 def system_methodHelp(self, method_name): 97 method = None 98 if method_name in self.funcs: 99 method = self.funcs[method_name] 100 elif self.instance is not None: 101 if hasattr(self.instance, '_methodHelp'): 102 return self.instance._methodHelp(method_name) 103 if not hasattr(self.instance, '_dispatch'): 104 try: 105 method = resolve_dotted_attribute(self.instance, method_name, self.allow_dotted_names) 106 except AttributeError: 107 pass 108 109 if method is None: 110 return '' 111 try: 112 import pydoc 113 return pydoc.getdoc(method) 114 except ImportError: 115 return method.__doc__ 116 117 def system_multicall(self, call_list): 118 results = [] 119 for call in call_list: 120 method_name = call['methodName'] 121 params = call['params'] 122 try: 123 results.append([self._dispatch(method_name, params)]) 124 except Fault as fault: 125 results.append({'faultCode': fault.faultCode, 126 'faultString': fault.faultString}) 127 except: 128 exc_type, exc_value, exc_tb = sys.exc_info() 129 results.append({'faultCode': 1, 130 'faultString': '%s:%s' % (exc_type, exc_value)}) 131 132 return results 133 134 def _dispatch(self, method, params): 135 func = None 136 try: 137 func = self.funcs[method] 138 except KeyError: 139 if self.instance is not None: 140 if hasattr(self.instance, '_dispatch'): 141 return self.instance._dispatch(method, params) 142 try: 143 func = resolve_dotted_attribute(self.instance, method, self.allow_dotted_names) 144 except AttributeError: 145 pass 146 147 if func is not None: 148 return func(*params) 149 raise Exception('method "%s" is not supported' % method) 150 151 152 class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): 153 rpc_paths = ('/', '/RPC2') 154 encode_threshold = 1400 155 wbufsize = -1 156 disable_nagle_algorithm = True 157 aepattern = re.compile('\n \\s* ([^\\s;]+) \\s* #content-coding\n (;\\s* q \\s*=\\s* ([0-9\\.]+))? #q\n ', re.VERBOSE | re.IGNORECASE) 158 159 def accept_encodings(self): 160 r = {} 161 ae = self.headers.get('Accept-Encoding', '') 162 for e in ae.split(','): 163 match = self.aepattern.match(e) 164 if match: 165 v = match.group(3) 166 v = float(v) if v else 1.0 167 r[match.group(1)] = v 168 169 return r 170 171 def is_rpc_path_valid(self): 172 if self.rpc_paths: 173 return self.path in self.rpc_paths 174 else: 175 return True 176 177 def do_POST(self): 178 if not self.is_rpc_path_valid(): 179 self.report_404() 180 return 181 try: 182 max_chunk_size = 10485760 183 size_remaining = int(self.headers['content-length']) 184 L = [] 185 while size_remaining: 186 chunk_size = min(size_remaining, max_chunk_size) 187 L.append(self.rfile.read(chunk_size)) 188 size_remaining -= len(L[-1]) 189 190 data = ''.join(L) 191 data = self.decode_request_content(data) 192 if data is None: 193 return 194 response = self.server._marshaled_dispatch(data, getattr(self, '_dispatch', None), self.path) 195 except Exception as e: 196 self.send_response(500) 197 if hasattr(self.server, '_send_traceback_header') and self.server._send_traceback_header: 198 self.send_header('X-exception', str(e)) 199 self.send_header('X-traceback', traceback.format_exc()) 200 self.send_header('Content-length', '0') 201 self.end_headers() 202 else: 203 self.send_response(200) 204 self.send_header('Content-type', 'text/xml') 205 if self.encode_threshold is not None: 206 if len(response) > self.encode_threshold: 207 q = self.accept_encodings().get('gzip', 0) 208 if q: 209 try: 210 response = xmlrpclib.gzip_encode(response) 211 self.send_header('Content-Encoding', 'gzip') 212 except NotImplementedError: 213 pass 214 215 self.send_header('Content-length', str(len(response))) 216 self.end_headers() 217 self.wfile.write(response) 218 219 def decode_request_content(self, data): 220 encoding = self.headers.get('content-encoding', 'identity').lower() 221 if encoding == 'identity': 222 return data 223 if encoding == 'gzip': 224 try: 225 return xmlrpclib.gzip_decode(data) 226 except NotImplementedError: 227 self.send_response(501, 'encoding %r not supported' % encoding) 228 except ValueError: 229 self.send_response(400, 'error decoding gzip content') 230 231 else: 232 self.send_response(501, 'encoding %r not supported' % encoding) 233 self.send_header('Content-length', '0') 234 self.end_headers() 235 236 def report_404(self): 237 self.send_response(404) 238 response = 'No such page' 239 self.send_header('Content-type', 'text/plain') 240 self.send_header('Content-length', str(len(response))) 241 self.end_headers() 242 self.wfile.write(response) 243 244 def log_request(self, code = '-', size = '-'): 245 if self.server.logRequests: 246 BaseHTTPServer.BaseHTTPRequestHandler.log_request(self, code, size) 247 248 249 class SimpleXMLRPCServer(SocketServer.TCPServer, SimpleXMLRPCDispatcher): 250 allow_reuse_address = True 251 _send_traceback_header = False 252 253 def __init__(self, addr, requestHandler = SimpleXMLRPCRequestHandler, logRequests = True, allow_none = False, encoding = None, bind_and_activate = True): 254 self.logRequests = logRequests 255 SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding) 256 SocketServer.TCPServer.__init__(self, addr, requestHandler, bind_and_activate) 257 if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'): 258 flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD) 259 flags |= fcntl.FD_CLOEXEC 260 fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags) 261 262 263 class MultiPathXMLRPCServer(SimpleXMLRPCServer): 264 265 def __init__(self, addr, requestHandler = SimpleXMLRPCRequestHandler, logRequests = True, allow_none = False, encoding = None, bind_and_activate = True): 266 SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, allow_none, encoding, bind_and_activate) 267 self.dispatchers = {} 268 self.allow_none = allow_none 269 self.encoding = encoding 270 271 def add_dispatcher(self, path, dispatcher): 272 self.dispatchers[path] = dispatcher 273 return dispatcher 274 275 def get_dispatcher(self, path): 276 return self.dispatchers[path] 277 278 def _marshaled_dispatch(self, data, dispatch_method = None, path = None): 279 try: 280 response = self.dispatchers[path]._marshaled_dispatch(data, dispatch_method, path) 281 except: 282 exc_type, exc_value = sys.exc_info()[:2] 283 response = xmlrpclib.dumps(xmlrpclib.Fault(1, '%s:%s' % (exc_type, exc_value)), encoding=self.encoding, allow_none=self.allow_none) 284 285 return response 286 287 288 class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher): 289 290 def __init__(self, allow_none = False, encoding = None): 291 SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding) 292 293 def handle_xmlrpc(self, request_text): 294 response = self._marshaled_dispatch(request_text) 295 print 'Content-Type: text/xml' 296 print 'Content-Length: %d' % len(response) 297 print 298 sys.stdout.write(response) 299 300 def handle_get(self): 301 code = 400 302 message, explain = BaseHTTPServer.BaseHTTPRequestHandler.responses[code] 303 response = BaseHTTPServer.DEFAULT_ERROR_MESSAGE % {'code': code, 304 'message': message, 305 'explain': explain} 306 print 'Status: %d %s' % (code, message) 307 print 'Content-Type: %s' % BaseHTTPServer.DEFAULT_ERROR_CONTENT_TYPE 308 print 'Content-Length: %d' % len(response) 309 print 310 sys.stdout.write(response) 311 312 def handle_request(self, request_text = None): 313 if request_text is None and os.environ.get('REQUEST_METHOD', None) == 'GET': 314 self.handle_get() 315 else: 316 try: 317 length = int(os.environ.get('CONTENT_LENGTH', None)) 318 except (TypeError, ValueError): 319 length = -1 320 321 if request_text is None: 322 request_text = sys.stdin.read(length) 323 self.handle_xmlrpc(request_text) 324 325 326 if __name__ == '__main__': 327 print 'Running XML-RPC server on port 8000' 328 server = SimpleXMLRPCServer(('localhost', 8000)) 329 server.register_function(pow) 330 server.register_function(lambda x, y: x + y, 'add') 331 server.serve_forever()