2 from jsonrpclib import Fault
3 from jsonrpclib.jsonrpc import USE_UNIX_SOCKETS
4 import SimpleXMLRPCServer
20 def get_version(request):
22 if 'jsonrpc' in request.keys():
24 if 'id' in request.keys():
28 def validate_request(request):
29 if type(request) is not types.DictType:
31 -32600, 'Request must be {}, not %s.' % type(request)
34 rpcid = request.get('id', None)
35 version = get_version(request)
37 fault = Fault(-32600, 'Request %s invalid.' % request, rpcid=rpcid)
39 request.setdefault('params', [])
40 method = request.get('method', None)
41 params = request.get('params')
42 param_types = (types.ListType, types.DictType, types.TupleType)
43 if not method or type(method) not in types.StringTypes or \
44 type(params) not in param_types:
46 -32600, 'Invalid request parameters or method.', rpcid=rpcid
51 class StratumJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
53 def __init__(self, encoding=None):
54 SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self,
58 def _marshaled_dispatch(self, data, dispatch_method = None):
61 request = jsonrpclib.loads(data)
63 fault = Fault(-32700, 'Request %s invalid. (%s)' % (data, e))
64 response = fault.response()
68 if type(request) is not types.ListType:
71 for req_entry in request:
72 result = validate_request(req_entry)
73 if type(result) is Fault:
74 responses.append(result.response())
76 resp_entry = self._marshaled_single_dispatch(req_entry)
77 if resp_entry is not None:
78 responses.append(resp_entry)
81 r = self._marshaled_single_dispatch({'method':'session.poll', 'params':[], 'id':'z' })
82 r = jsonrpclib.loads(r)
85 responses.append(json.dumps(item))
87 if len(responses) > 1:
88 response = '[%s]' % ','.join(responses)
89 elif len(responses) == 1:
90 response = responses[0]
96 def _marshaled_single_dispatch(self, request):
97 # TODO - Use the multiprocessing and skip the response if
98 # it is a notification
99 # Put in support for custom dispatcher here
100 # (See SimpleXMLRPCServer._marshaled_dispatch)
101 method = request.get('method')
102 params = request.get('params')
103 if params is None: params=[]
104 params = [ self.session_id, request['id'] ] + params
105 #print method, params
107 response = self._dispatch(method, params)
109 exc_type, exc_value, exc_tb = sys.exc_info()
110 fault = Fault(-32603, '%s:%s' % (exc_type, exc_value))
111 return fault.response()
112 if 'id' not in request.keys() or request['id'] == None:
113 # It's a notification
117 response = jsonrpclib.dumps(response,
123 exc_type, exc_value, exc_tb = sys.exc_info()
124 fault = Fault(-32603, '%s:%s' % (exc_type, exc_value))
125 return fault.response()
127 def _dispatch(self, method, params):
130 func = self.funcs[method]
132 if self.instance is not None:
133 if hasattr(self.instance, '_dispatch'):
134 return self.instance._dispatch(method, params)
137 func = SimpleXMLRPCServer.resolve_dotted_attribute(
142 except AttributeError:
146 if type(params) is types.ListType:
147 response = func(*params)
149 response = func(**params)
152 return Fault(-32602, 'Invalid parameters.')
154 err_lines = traceback.format_exc().splitlines()
155 trace_string = '%s | %s' % (err_lines[-3], err_lines[-1])
156 fault = jsonrpclib.Fault(-32603, 'Server error: %s' %
160 return Fault(-32601, 'Method %s not supported.' % method)
162 class StratumJSONRPCRequestHandler(
163 SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
166 if not self.is_rpc_path_valid():
170 self.server.session_id = None
171 c = self.headers.get('cookie')
173 if c[0:8]=='SESSION=':
174 #print "found cookie", c[8:]
175 self.server.session_id = c[8:]
177 if self.server.session_id is None:
178 r = self.server._marshaled_single_dispatch({'method':'session.create', 'params':[], 'id':'z' })
179 r = jsonrpclib.loads(r)
180 self.server.session_id = r.get('result')
181 #print "setting cookie", self.server.session_id
183 data = json.dumps([])
184 response = self.server._marshaled_dispatch(data)
185 self.send_response(200)
187 self.send_response(500)
188 err_lines = traceback.format_exc().splitlines()
189 trace_string = '%s | %s' % (err_lines[-3], err_lines[-1])
190 fault = jsonrpclib.Fault(-32603, 'Server error: %s' % trace_string)
191 response = fault.response()
192 print "500", trace_string
196 if hasattr(self.server, 'session_id'):
197 if self.server.session_id:
198 self.send_header("Set-Cookie", "SESSION=%s"%self.server.session_id)
199 self.session_id = None
201 self.send_header("Content-type", "application/json-rpc")
202 self.send_header("Content-length", str(len(response)))
204 self.wfile.write(response)
206 self.connection.shutdown(1)
210 if not self.is_rpc_path_valid():
214 max_chunk_size = 10*1024*1024
215 size_remaining = int(self.headers["content-length"])
217 while size_remaining:
218 chunk_size = min(size_remaining, max_chunk_size)
219 L.append(self.rfile.read(chunk_size))
220 size_remaining -= len(L[-1])
223 self.server.session_id = None
224 c = self.headers.get('cookie')
226 if c[0:8]=='SESSION=':
227 print "found cookie", c[8:]
228 self.server.session_id = c[8:]
230 if self.server.session_id is None:
231 r = self.server._marshaled_single_dispatch({'method':'session.create', 'params':[], 'id':'z' })
232 r = jsonrpclib.loads(r)
233 self.server.session_id = r.get('result')
234 #print "setting cookie", self.server.session_id
236 response = self.server._marshaled_dispatch(data)
237 self.send_response(200)
239 self.send_response(500)
240 err_lines = traceback.format_exc().splitlines()
241 trace_string = '%s | %s' % (err_lines[-3], err_lines[-1])
242 fault = jsonrpclib.Fault(-32603, 'Server error: %s' % trace_string)
243 response = fault.response()
244 print "500", trace_string
248 if hasattr(self.server, 'session_id'):
249 if self.server.session_id:
250 self.send_header("Set-Cookie", "SESSION=%s"%self.server.session_id)
251 self.session_id = None
253 self.send_header("Content-type", "application/json-rpc")
254 self.send_header("Content-length", str(len(response)))
256 self.wfile.write(response)
258 self.connection.shutdown(1)
261 class StratumJSONRPCServer(SocketServer.TCPServer, StratumJSONRPCDispatcher):
263 allow_reuse_address = True
265 def __init__(self, addr, requestHandler=StratumJSONRPCRequestHandler,
266 logRequests=True, encoding=None, bind_and_activate=True,
267 address_family=socket.AF_INET):
268 self.logRequests = logRequests
269 StratumJSONRPCDispatcher.__init__(self, encoding)
270 # TCPServer.__init__ has an extra parameter on 2.6+, so
271 # check Python version and decide on how to call it
272 vi = sys.version_info
273 self.address_family = address_family
274 if USE_UNIX_SOCKETS and address_family == socket.AF_UNIX:
275 # Unix sockets can't be bound if they already exist in the
276 # filesystem. The convention of e.g. X11 is to unlink
277 # before binding again.
278 if os.path.exists(addr):
282 logging.warning("Could not unlink socket %s", addr)
283 # if python 2.5 and lower
284 if vi[0] < 3 and vi[1] < 6:
285 SocketServer.TCPServer.__init__(self, addr, requestHandler)
287 SocketServer.TCPServer.__init__(self, addr, requestHandler,
289 if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
290 flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
291 flags |= fcntl.FD_CLOEXEC
292 fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)