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 from p2pool.util import deferred_resource, memoize
12 class Error(Exception):
13 def __init__(self, code, message, data=None):
14 if type(self) is Error:
15 raise TypeError("can't directly instantiate Error class; use Error_for_code")
16 if not isinstance(code, int):
17 raise TypeError('code must be an int')
18 #if not isinstance(message, unicode):
19 # raise TypeError('message must be a unicode')
20 self.code, self.message, self.data = code, message, data
22 return '%i %s' % (self.code, self.message) + (' %r' % (self.data, ) if self.data is not None else '')
26 'message': self.message,
30 @memoize.memoize_with_backing(weakref.WeakValueDictionary())
31 def Error_for_code(code):
32 class NarrowError(Error):
33 def __init__(self, *args, **kwargs):
34 Error.__init__(self, code, *args, **kwargs)
39 def __init__(self, func, services=[]):
41 self._services = services
43 def __getattr__(self, attr):
44 if attr.startswith('rpc_'):
45 return lambda *params: self._func('.'.join(self._services + [attr[len('rpc_'):]]), params)
46 elif attr.startswith('svc_'):
47 return Proxy(self._func, self._services + [attr[len('svc_'):]])
49 raise AttributeError('%r object has no attribute %r' % (self.__class__.__name__, attr))
51 @defer.inlineCallbacks
52 def _handle(data, provider, preargs=(), response_handler=None):
58 req = json.loads(data)
60 raise Error_for_code(-32700)(u'Parse error')
62 if 'result' in req or 'error' in req:
63 response_handler(req['id'], req['result'] if 'error' not in req or req['error'] is None else
64 failure.Failure(Error_for_code(resp['error']['code'])(resp['error']['message'], resp['error'].get('data', None))))
65 defer.returnValue(None)
67 id_ = req.get('id', None)
68 method = req.get('method', None)
69 if not isinstance(method, basestring):
70 raise Error_for_code(-32600)(u'Invalid Request')
71 params = req.get('params', [])
72 if not isinstance(params, list):
73 raise Error_for_code(-32600)(u'Invalid Request')
75 for service_name in method.split('.')[:-1]:
76 provider = getattr(provider, 'svc_' + service_name, None)
78 raise Error_for_code(-32601)(u'Service not found')
80 method_meth = getattr(provider, 'rpc_' + method.split('.')[-1], None)
81 if method_meth is None:
82 raise Error_for_code(-32601)(u'Method not found')
84 result = yield method_meth(*list(preargs) + list(params))
89 log.err(None, 'Squelched JSON error:')
90 raise Error_for_code(-32099)(u'Unknown error')
95 defer.returnValue(json.dumps(dict(
104 @defer.inlineCallbacks
105 def _http_do(url, headers, timeout, method, params):
109 data = yield client.getPage(
112 headers=dict(headers, **{'Content-Type': 'application/json'}),
113 postdata=json.dumps({
121 except error.Error, e:
123 resp = json.loads(e.response)
127 resp = json.loads(data)
129 if resp['id'] != id_:
130 raise ValueError('invalid id')
131 if 'error' in resp and resp['error'] is not None:
132 raise Error_for_code(resp['error']['code'])(resp['error']['message'], resp['error'].get('data', None))
133 defer.returnValue(resp['result'])
134 HTTPProxy = lambda url, headers={}, timeout=5: Proxy(lambda method, params: _http_do(url, headers, timeout, method, params))
136 class HTTPServer(deferred_resource.DeferredResource):
137 def __init__(self, provider):
138 deferred_resource.DeferredResource.__init__(self)
139 self._provider = provider
141 @defer.inlineCallbacks
142 def render_POST(self, request):
143 data = yield _handle(request.content.read(), self._provider, preargs=[request])
144 assert data is not None
145 request.setHeader('Content-Type', 'application/json')
146 request.setHeader('Content-Length', len(data))