1 from __future__ import division
6 from twisted.internet import defer
7 from twisted.python import log
8 from twisted.web import client, error
10 import deferred_resource
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
20 return '%i %s' % (self.code, self.message) + (' %r' % (self.data, ) if self.data is not None else '')
24 'message': self.message,
29 def __init__(self, url, auth=None, timeout=5):
32 self._timeout = timeout
34 @defer.inlineCallbacks
35 def callRemote(self, method, *params):
39 'Content-Type': 'application/json',
41 if self._auth is not None:
42 headers['Authorization'] = 'Basic ' + base64.b64encode(':'.join(self._auth))
44 data = yield client.getPage(
54 timeout=self._timeout,
56 except error.Error, e:
58 resp = json.loads(e.response)
62 resp = json.loads(data)
65 raise ValueError('invalid id')
66 if 'error' in resp and resp['error'] is not None:
67 raise Error(**unicode(resp['error']))
68 defer.returnValue(resp['result'])
70 def __getattr__(self, attr):
71 if attr.startswith('rpc_'):
72 return lambda *params: self.callRemote(attr[len('rpc_'):], *params)
73 raise AttributeError('%r object has no attribute %r' % (self.__class__.__name__, attr))
75 class Server(deferred_resource.DeferredResource):
76 @defer.inlineCallbacks
77 def render_POST(self, request):
78 # missing batching, 1.0 notifications
83 data = request.content.read()
86 req = json.loads(data)
88 raise Error(-32700, u'Parse error')
91 id_ = req.get('id', None)
92 method = req['method']
93 if not isinstance(method, basestring):
95 params = req.get('params', [])
96 if not isinstance(params, list):
99 raise Error(-32600, u'Invalid Request')
101 method_meth = getattr(self, 'rpc_' + method, None)
102 if method_meth is None:
103 raise Error(-32601, u'Method not found')
105 result = yield method_meth(request, *params)
115 log.err(None, 'Squelched JSON error:')
118 error = Error(-32099, u'Unknown error')._to_obj()
120 data = json.dumps(dict(
126 request.setHeader('Content-Type', 'application/json')
127 request.setHeader('Content-Length', len(data))