2 from jsonrpclib import Fault
3 import SimpleXMLRPCServer
10 def get_version(request):
12 if 'jsonrpc' in request.keys():
14 if 'id' in request.keys():
18 def validate_request(request):
19 if type(request) is not types.DictType:
21 -32600, 'Request must be {}, not %s.' % type(request)
24 rpcid = request.get('id', None)
25 version = get_version(request)
27 fault = Fault(-32600, 'Request %s invalid.' % request, rpcid=rpcid)
29 request.setdefault('params', [])
30 method = request.get('method', None)
31 params = request.get('params')
32 param_types = (types.ListType, types.DictType, types.TupleType)
33 if not method or type(method) not in types.StringTypes or \
34 type(params) not in param_types:
36 -32600, 'Invalid request parameters or method.', rpcid=rpcid
41 class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
43 def __init__(self, encoding=None):
44 SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self,
48 def _marshaled_dispatch(self, data, dispatch_method = None):
51 request = jsonrpclib.loads(data)
53 fault = Fault(-32700, 'Request %s invalid. (%s)' % (data, e))
54 response = fault.response()
57 fault = Fault(-32600, 'Request invalid -- no request data.')
58 return fault.response()
59 if type(request) is types.ListType:
60 # This SHOULD be a batch, by spec
62 for req_entry in request:
63 result = validate_request(req_entry)
64 if type(result) is Fault:
65 responses.append(result.response())
67 resp_entry = self._marshaled_single_dispatch(req_entry)
68 if resp_entry is not None:
69 responses.append(resp_entry)
70 if len(responses) > 0:
71 response = '[%s]' % ','.join(responses)
75 result = validate_request(request)
76 if type(result) is Fault:
77 return result.response()
78 response = self._marshaled_single_dispatch(request)
81 def _marshaled_single_dispatch(self, request):
82 # TODO - Use the multiprocessing and skip the response if
83 # it is a notification
84 # Put in support for custom dispatcher here
85 # (See SimpleXMLRPCServer._marshaled_dispatch)
86 method = request.get('method')
87 params = request.get('params')
89 response = self._dispatch(method, params)
91 exc_type, exc_value, exc_tb = sys.exc_info()
92 fault = Fault(-32603, '%s:%s' % (exc_type, exc_value))
93 return fault.response()
94 if 'id' not in request.keys() or request['id'] == None:
98 response = jsonrpclib.dumps(response,
104 exc_type, exc_value, exc_tb = sys.exc_info()
105 fault = Fault(-32603, '%s:%s' % (exc_type, exc_value))
106 return fault.response()
108 def _dispatch(self, method, params):
111 func = self.funcs[method]
113 if self.instance is not None:
114 if hasattr(self.instance, '_dispatch'):
115 return self.instance._dispatch(method, params)
118 func = SimpleXMLRPCServer.resolve_dotted_attribute(
123 except AttributeError:
127 if type(params) is types.ListType:
128 response = func(*params)
130 response = func(**params)
133 return Fault(-32602, 'Invalid parameters.')
135 err_lines = traceback.format_exc().splitlines()
136 trace_string = '%s | %s' % (err_lines[-3], err_lines[-1])
137 fault = jsonrpclib.Fault(-32603, 'Server error: %s' %
141 return Fault(-32601, 'Method %s not supported.' % method)
143 class SimpleJSONRPCRequestHandler(
144 SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
147 if not self.is_rpc_path_valid():
151 max_chunk_size = 10*1024*1024
152 size_remaining = int(self.headers["content-length"])
154 while size_remaining:
155 chunk_size = min(size_remaining, max_chunk_size)
156 L.append(self.rfile.read(chunk_size))
157 size_remaining -= len(L[-1])
159 response = self.server._marshaled_dispatch(data)
160 self.send_response(200)
162 self.send_response(500)
163 err_lines = traceback.format_exc().splitlines()
164 trace_string = '%s | %s' % (err_lines[-3], err_lines[-1])
165 fault = jsonrpclib.Fault(-32603, 'Server error: %s' % trace_string)
166 response = fault.response()
169 self.send_header("Content-type", "application/json-rpc")
170 self.send_header("Content-length", str(len(response)))
172 self.wfile.write(response)
174 self.connection.shutdown(1)
176 class SimpleJSONRPCServer(SocketServer.TCPServer,
177 SimpleJSONRPCDispatcher):
179 allow_reuse_address = True
181 def __init__(self, addr, requestHandler=SimpleJSONRPCRequestHandler,
182 logRequests=True, encoding=None, bind_and_activate=True):
183 self.logRequests = logRequests
184 SimpleJSONRPCDispatcher.__init__(self, encoding)
185 # TCPServer.__init__ has an extra parameter on 2.6+, so
186 # check Python version and decide on how to call it
187 vi = sys.version_info
188 # if python 2.5 and lower
189 if vi[0] < 3 and vi[1] < 6:
190 SocketServer.TCPServer.__init__(self, addr, requestHandler)
192 SocketServer.TCPServer.__init__(self, addr, requestHandler,
194 if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
195 flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
196 flags |= fcntl.FD_CLOEXEC
197 fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
199 class CGIJSONRPCRequestHandler(SimpleJSONRPCDispatcher):
201 def __init__(self, encoding=None):
202 SimpleJSONRPCDispatcher.__init__(self, encoding)
204 def handle_jsonrpc(self, request_text):
205 response = self._marshaled_dispatch(request_text)
206 print 'Content-Type: application/json-rpc'
207 print 'Content-Length: %d' % len(response)
209 sys.stdout.write(response)
211 handle_xmlrpc = handle_jsonrpc
213 if __name__ == '__main__':
214 print 'Running JSON-RPC server on port 8000'
215 server = SimpleJSONRPCServer(("localhost", 8000))
216 server.register_function(pow)
217 server.register_function(lambda x,y: x+y, 'add')
218 server.serve_forever()