fixed jsonrpc.Server superclass constructor not being called, leading to strange...
[p2pool.git] / p2pool / util / jsonrpc.py
1 from __future__ import division
2
3 import json
4
5 from twisted.internet import defer
6 from twisted.python import log
7 from twisted.web import client, error
8
9 import deferred_resource
10
11 class Error(Exception):
12     def __init__(self, code, message, data=None):
13         if not isinstance(code, int):
14             raise TypeError('code must be an int')
15         #if not isinstance(message, unicode):
16         #    raise TypeError('message must be a unicode')
17         self.code, self.message, self.data = code, message, data
18     def __str__(self):
19         return '%i %s' % (self.code, self.message) + (' %r' % (self.data, ) if self.data is not None else '')
20     def _to_obj(self):
21         return {
22             'code': self.code,
23             'message': self.message,
24             'data': self.data,
25         }
26
27 class Proxy(object):
28     def __init__(self, url, headers={}, timeout=5):
29         self._url = url
30         self._headers = headers
31         self._timeout = timeout
32     
33     @defer.inlineCallbacks
34     def callRemote(self, method, *params):
35         id_ = 0
36         
37         try:
38             data = yield client.getPage(
39                 url=self._url,
40                 method='POST',
41                 headers=dict(self._headers, **{'Content-Type': 'application/json'}),
42                 postdata=json.dumps({
43                     'jsonrpc': '2.0',
44                     'method': method,
45                     'params': params,
46                     'id': id_,
47                 }),
48                 timeout=self._timeout,
49             )
50         except error.Error, e:
51             try:
52                 resp = json.loads(e.response)
53             except:
54                 raise e
55         else:
56             resp = json.loads(data)
57         
58         if resp['id'] != id_:
59             raise ValueError('invalid id')
60         if 'error' in resp and resp['error'] is not None:
61             raise Error(**resp['error'])
62         defer.returnValue(resp['result'])
63     
64     def __getattr__(self, attr):
65         if attr.startswith('rpc_'):
66             return lambda *params: self.callRemote(attr[len('rpc_'):], *params)
67         raise AttributeError('%r object has no attribute %r' % (self.__class__.__name__, attr))
68
69 class Server(deferred_resource.DeferredResource):
70     def __init__(self, provider):
71         deferred_resource.DeferredResource.__init__(self)
72         self._provider = provider
73     
74     @defer.inlineCallbacks
75     def render_POST(self, request):
76         id_ = None
77         
78         try:
79             try:
80                 data = request.content.read()
81                 
82                 try:
83                     req = json.loads(data)
84                 except Exception:
85                     raise Error(-32700, u'Parse error')
86                 
87                 id_ = req.get('id', None)
88                 method = req.get('method', None)
89                 if not isinstance(method, basestring):
90                     raise Error(-32600, u'Invalid Request')
91                 params = req.get('params', [])
92                 if not isinstance(params, list):
93                     raise Error(-32600, u'Invalid Request')
94                 
95                 method_meth = getattr(self._provider, 'rpc_' + method, None)
96                 if method_meth is None:
97                     raise Error(-32601, u'Method not found')
98                 
99                 result = yield method_meth(request, *params)
100                 error = None
101             except Error:
102                 raise
103             except Exception:
104                 log.err(None, 'Squelched JSON error:')
105                 raise Error(-32099, u'Unknown error')
106         except Error, e:
107             result = None
108             error = e._to_obj()
109         
110         data = json.dumps(dict(
111             jsonrpc='2.0',
112             id=id_,
113             result=result,
114             error=error,
115         ))
116         request.setHeader('Content-Type', 'application/json')
117         request.setHeader('Content-Length', len(data))
118         request.write(data)