X-Git-Url: http://xvm.mit.edu/gitweb/invirt/packages/python-jsonrpclib.git/blobdiff_plain/ccb16ea1a5f000b5014f562467056e788ff7eed4..669c1369c82b5dec6fd7952dce27a41ac1690b11:/jsonrpclib/SimpleJSONRPCServer.py diff --git a/jsonrpclib/SimpleJSONRPCServer.py b/jsonrpclib/SimpleJSONRPCServer.py new file mode 100644 index 0000000..ae41f93 --- /dev/null +++ b/jsonrpclib/SimpleJSONRPCServer.py @@ -0,0 +1,188 @@ +import jsonrpclib +from jsonrpclib import Fault +import SimpleXMLRPCServer +import SocketServer +import types +import traceback +import fcntl +import sys + +def get_version(request): + if type(request) not in (types.ListType, types.DictType): + return None + if type(request) is types.ListType: + if len(request) == 0: + return None + if 'jsonrpc' not in request[0].keys(): + return None + return '2.0' + # must be a dict + if 'jsonrpc' in request.keys(): + return 2.0 + if 'id' in request.keys(): + return 1.0 + return None + +class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher): + + def __init__(self, encoding=None): + SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self, + allow_none=True, + encoding=encoding) + + def _marshaled_dispatch(self, data, dispatch_method = None): + response = None + try: + request = jsonrpclib.loads(data) + except: + fault = Fault(-32600, 'Request %s invalid.' % data) + response = fault.response() + return response + version = get_version(request) + if not version: + fault = Fault(-32600, 'Request %s invalid.' % data) + response = fault.response() + return response + if type(request) is types.ListType: + # This SHOULD be a batch, by spec + responses = [] + for req_entry in request: + resp_entry = self._marshaled_single_dispatch(req_entry) + if resp_entry is not None: + responses.append(resp_entry) + response = '[%s]' % ','.join(responses) + else: + response = self._marshaled_single_dispatch(request) + return response + + def _marshaled_single_dispatch(self, request): + # TODO - Use the multiprocessing and skip the response if + # it is a notification + method = request['method'] + params = request['params'] + # Put in support for custom dispatcher here + # (See SimpleXMLRPCServer._marshaled_dispatch) + try: + response = self._dispatch(method, params) + except: + exc_type, exc_value, exc_tb = sys.exc_info() + fault = Fault(-32603, '%s:%s' % (exc_type, exc_value)) + return fault.response() + if 'id' not in request.keys() or request['id'] == None: + # It's a notification + return None + try: + response = jsonrpclib.dumps(response, + methodresponse=True, + rpcid=request['id'] + ) + return response + except: + exc_type, exc_value, exc_tb = sys.exc_info() + fault = Fault(-32603, '%s:%s' % (exc_type, exc_value)) + return fault.response() + + def _dispatch(self, method, params): + func = None + try: + func = self.funcs[method] + except KeyError: + if self.instance is not None: + if hasattr(self.instance, '_dispatch'): + return self.instance._dispatch(method, params) + else: + try: + func = resolve_dotted_attribute( + self.instance, + method, + True + ) + except AttributeError: + pass + if func is not None: + try: + if type(params) is types.ListType: + response = func(*params) + else: + response = func(**params) + return response + except TypeError: + return Fault(-32602, 'Invalid parameters.') + except: + err_lines = traceback.format_exc().splitlines() + trace_string = '%s | %s' % (err_lines[-3], err_lines[-1]) + fault = jsonrpclib.Fault(-32603, 'Server error: %s' % + trace_string) + return fault + else: + return Fault(-32601, 'Method %s not supported.' % method) + +class SimpleJSONRPCRequestHandler( + SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): + + def do_POST(self): + if not self.is_rpc_path_valid(): + self.report_404() + return + try: + max_chunk_size = 10*1024*1024 + size_remaining = int(self.headers["content-length"]) + L = [] + while size_remaining: + chunk_size = min(size_remaining, max_chunk_size) + L.append(self.rfile.read(chunk_size)) + size_remaining -= len(L[-1]) + data = ''.join(L) + response = self.server._marshaled_dispatch(data) + self.send_response(200) + except Exception, e: + self.send_response(500) + err_lines = traceback.format_exc().splitlines() + trace_string = '%s | %s' % (err_lines[-3], err_lines[-1]) + fault = jsonrpclib.Fault(-32603, 'Server error: %s' % trace_string) + response = fault.response() + if response == None: + response = '' + self.send_header("Content-type", "application/json-rpc") + self.send_header("Content-length", str(len(response))) + self.end_headers() + self.wfile.write(response) + self.wfile.flush() + self.connection.shutdown(1) + +class SimpleJSONRPCServer(SocketServer.TCPServer, + SimpleJSONRPCDispatcher): + + allow_reuse_address = True + + def __init__(self, addr, requestHandler=SimpleJSONRPCRequestHandler, + logRequests=True, encoding=None, bind_and_activate=True): + self.logRequests = logRequests + SimpleJSONRPCDispatcher.__init__(self, encoding) + SocketServer.TCPServer.__init__(self, addr, requestHandler, + bind_and_activate) + if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'): + flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD) + flags |= fcntl.FD_CLOEXEC + fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags) + +class CGIJSONRPCRequestHandler(SimpleJSONRPCDispatcher): + + def __init__(self, encoding=None): + SimpleJSONRPCDispatcher.__init__(self, encoding) + + def handle_jsonrpc(self, request_text): + response = self._marshaled_dispatch(request_text) + print 'Content-Type: application/json-rpc' + print 'Content-Length: %d' % len(response) + print + sys.stdout.write(response) + + handle_xmlrpc = handle_jsonrpc + +if __name__ == '__main__': + print 'Running JSON-RPC server on port 8000' + server = SimpleJSONRPCServer(("localhost", 8000)) + server.register_function(pow) + server.register_function(lambda x,y: x+y, 'add') + server.serve_forever()