1eb33d010e1757ca1de7b92e23ecae1873eb5289
[p2pool.git] / jsonrpc.py
1 from __future__ import division
2
3 import base64
4 import json
5 import traceback
6
7 from twisted.internet import defer
8 from twisted.web import client
9
10 import util
11
12 class Error(Exception):
13     def __init__(self, code, message, data=None):
14         if not isinstance(code, int):
15             raise TypeError('code must be an int')
16         if not isinstance(message, unicode):
17             raise TypeError('message must be a unicode')
18         self._code, self._message, self._data = code, message, data
19     def __str__(self):
20         return '%i %s %r' % (self._code, self._message, self._data)
21     def _to_obj(self):
22         return {
23             'code': self._code,
24             'message': self._message,
25             'data': self._data,
26         }
27
28 class Proxy(object):
29     def __init__(self, url, auth=None):
30         self._url = url
31         self._auth = auth
32     
33     @defer.inlineCallbacks
34     def callRemote(self, method, *params):
35         id_ = 0
36         
37         headers = {
38             'Content-Type': 'text/json',
39         }
40         if self._auth is not None:
41             headers['Authorization'] = 'Basic ' + base64.b64encode(':'.join(self._auth))
42         resp = json.loads((yield client.getPage(
43             url=self._url,
44             method='POST',
45             headers=headers,
46             postdata=json.dumps({
47                 'jsonrpc': '2.0',
48                 'method': method,
49                 'params': params,
50                 'id': id_,
51             }),
52         )))
53         
54         if resp['id'] != id_:
55             raise ValueError('invalid id')
56         if 'error' in resp and resp['error'] is not None:
57             raise Error(resp['error'])
58         defer.returnValue(resp['result'])
59     
60     def __getattr__(self, attr):
61         if attr.startswith('rpc_'):
62             return lambda *params: self.callRemote(attr[len('rpc_'):], *params)
63         raise AttributeError('%r object has no attribute %r' % (self.__class__.__name__, attr))
64
65 class Server(util.DeferredResource):
66     extra_headers = None
67     
68     @defer.inlineCallbacks
69     def render_POST(self, request):
70         # missing batching, 1.0 notifications
71         data = request.content.read()
72         
73         if self.extra_headers is not None:
74             for name, value in self.extra_headers.iteritems():
75                 request.setHeader(name, value)
76         
77         try:
78             try:
79                 req = json.loads(data)
80             except Exception:
81                 raise RemoteError(-32700, u'Parse error')
82         except Error, e:
83             # id unknown
84             request.write(json.dumps({
85                 'jsonrpc': '2.0',
86                 'id': None,
87                 'result': None,
88                 'error': e._to_obj(),
89             }))
90         
91         id_ = req.get('id', None)
92         
93         try:
94             try:
95                 method = req['method']
96                 if not isinstance(method, unicode):
97                     raise ValueError()
98                 params = req.get('params', [])
99                 if not isinstance(params, list):
100                     raise ValueError()
101             except Exception:
102                 raise Error(-32600, u'Invalid Request')
103             
104             method_name = 'rpc_' + method
105             if not hasattr(self, method_name):
106                 raise Error(-32601, u'Method not found')
107             method_meth = getattr(self, method_name)
108             
109             df = defer.maybeDeferred(method_meth, *params)
110             
111             if id_ is None:
112                 return
113             
114             try:
115                 result = yield df
116             except Error, e:
117                 raise e
118             except Exception, e:
119                 print 'Squelched JSON method error:'
120                 traceback.print_exc()
121                 raise Error(-32099, u'Unknown error')
122             
123             request.write(json.dumps({
124                 'jsonrpc': '2.0',
125                 'id': id_,
126                 'result': result,
127                 'error': None,
128             }))
129         except Error, e:
130             request.write(json.dumps({
131                 'jsonrpc': '2.0',
132                 'id': id_,
133                 'result': None,
134                 'error': e._to_obj(),
135             }))