refactored jsonrpc: broke generic jsonrpc handling out of HTTP-specific implementatio...
[p2pool.git] / p2pool / util / jsonrpc.py
index aa334a6..35c1ec6 100644 (file)
@@ -34,66 +34,36 @@ def Error_for_code(code):
             Error.__init__(self, code, *args, **kwargs)
     return NarrowError
 
+
 class Proxy(object):
-    def __init__(self, url, headers={}, timeout=5):
-        self._url = url
-        self._headers = headers
-        self._timeout = timeout
-    
-    @defer.inlineCallbacks
-    def callRemote(self, method, *params):
-        id_ = 0
-        
-        try:
-            data = yield client.getPage(
-                url=self._url,
-                method='POST',
-                headers=dict(self._headers, **{'Content-Type': 'application/json'}),
-                postdata=json.dumps({
-                    'jsonrpc': '2.0',
-                    'method': method,
-                    'params': params,
-                    'id': id_,
-                }),
-                timeout=self._timeout,
-            )
-        except error.Error, e:
-            try:
-                resp = json.loads(e.response)
-            except:
-                raise e
-        else:
-            resp = json.loads(data)
-        
-        if resp['id'] != id_:
-            raise ValueError('invalid id')
-        if 'error' in resp and resp['error'] is not None:
-            raise Error_for_code(resp['error']['code'])(resp['error']['message'], resp['error'].get('data', None))
-        defer.returnValue(resp['result'])
+    def __init__(self, func, services=[]):
+        self._func = func
+        self._services = services
     
     def __getattr__(self, attr):
         if attr.startswith('rpc_'):
-            return lambda *params: self.callRemote(attr[len('rpc_'):], *params)
-        raise AttributeError('%r object has no attribute %r' % (self.__class__.__name__, attr))
+            return lambda *params: self._func('.'.join(self._services + [attr[len('rpc_'):]]), params)
+        elif attr.startswith('svc_'):
+            return Proxy(self._func, self._services + [attr[len('svc_'):]])
+        else:
+            raise AttributeError('%r object has no attribute %r' % (self.__class__.__name__, attr))
 
-class Server(deferred_resource.DeferredResource):
-    def __init__(self, provider):
-        deferred_resource.DeferredResource.__init__(self)
-        self._provider = provider
-    
-    @defer.inlineCallbacks
-    def render_POST(self, request):
+@defer.inlineCallbacks
+def _handle(data, provider, preargs=(), response_handler=None):
         id_ = None
         
         try:
             try:
-                data = request.content.read()
-                
                 try:
                     req = json.loads(data)
                 except Exception:
                     raise Error_for_code(-32700)(u'Parse error')
                 
+                if 'result' in req or 'error' in req:
+                    response_handler(req['id'], req['result'] if 'error' not in req or req['error'] is None else
+                        failure.Failure(Error_for_code(resp['error']['code'])(resp['error']['message'], resp['error'].get('data', None))))
+                    defer.returnValue(None)
+                
                 id_ = req.get('id', None)
                 method = req.get('method', None)
                 if not isinstance(method, basestring):
@@ -102,11 +72,16 @@ class Server(deferred_resource.DeferredResource):
                 if not isinstance(params, list):
                     raise Error_for_code(-32600)(u'Invalid Request')
                 
-                method_meth = getattr(self._provider, 'rpc_' + method, None)
+                for service_name in method.split('.')[:-1]:
+                    provider = getattr(provider, 'svc_' + service_name, None)
+                    if provider is None:
+                        raise Error_for_code(-32601)(u'Service not found')
+                
+                method_meth = getattr(provider, 'rpc_' + method.split('.')[-1], None)
                 if method_meth is None:
                     raise Error_for_code(-32601)(u'Method not found')
                 
-                result = yield method_meth(request, *params)
+                result = yield method_meth(*list(preargs) + list(params))
                 error = None
             except Error:
                 raise
@@ -117,12 +92,56 @@ class Server(deferred_resource.DeferredResource):
             result = None
             error = e._to_obj()
         
-        data = json.dumps(dict(
+        defer.returnValue(json.dumps(dict(
             jsonrpc='2.0',
             id=id_,
             result=result,
             error=error,
-        ))
+        )))
+
+# HTTP
+
+@defer.inlineCallbacks
+def _http_do(url, headers, timeout, method, params):
+    id_ = 0
+    
+    try:
+        data = yield client.getPage(
+            url=url,
+            method='POST',
+            headers=dict(headers, **{'Content-Type': 'application/json'}),
+            postdata=json.dumps({
+                'jsonrpc': '2.0',
+                'method': method,
+                'params': params,
+                'id': id_,
+            }),
+            timeout=timeout,
+        )
+    except error.Error, e:
+        try:
+            resp = json.loads(e.response)
+        except:
+            raise e
+    else:
+        resp = json.loads(data)
+    
+    if resp['id'] != id_:
+        raise ValueError('invalid id')
+    if 'error' in resp and resp['error'] is not None:
+        raise Error_for_code(resp['error']['code'])(resp['error']['message'], resp['error'].get('data', None))
+    defer.returnValue(resp['result'])
+HTTPProxy = lambda url, headers={}, timeout=5: Proxy(lambda method, params: _http_do(url, headers, timeout, method, params))
+
+class HTTPServer(deferred_resource.DeferredResource):
+    def __init__(self, provider):
+        deferred_resource.DeferredResource.__init__(self)
+        self._provider = provider
+    
+    @defer.inlineCallbacks
+    def render_POST(self, request):
+        data = yield _handle(request.content.read(), self._provider, preargs=[request])
+        assert data is not None
         request.setHeader('Content-Type', 'application/json')
         request.setHeader('Content-Length', len(data))
         request.write(data)