diff wibble/server/JsonRpcApp.py @ 0:6a61cfdf6930

Initial version
author Ben Croston <ben@croston.org>
date Tue, 30 Aug 2011 22:19:48 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wibble/server/JsonRpcApp.py	Tue Aug 30 22:19:48 2011 +0100
@@ -0,0 +1,126 @@
+from webob import Request, Response, exc
+try:
+  from json import loads, dumps
+except:
+  from simplejson import loads, dumps
+import traceback
+import sys
+
+class JsonRpcApp(object):
+    """
+    Serve the given object via json-rpc (http://json-rpc.org/)
+    """
+
+    def __init__(self, obj, auth=None):
+        """
+        obj  - a class of functions available using jsonrpc
+        auth - an authentication function (optional)
+        """
+        self.obj = obj
+        self.auth = auth
+
+    def __call__(self, environ, start_response):
+        req = Request(environ)
+        try:
+            resp = self.process(req)
+        except ValueError, e:
+            resp = exc.HTTPBadRequest(str(e))
+        except exc.HTTPException, e:
+            resp = e
+        return resp(environ, start_response)
+
+    def process(self, req):
+        if not req.method == 'POST':
+            raise exc.HTTPMethodNotAllowed("Only POST allowed").exception
+
+        try:
+            json = loads(req.body)
+        except ValueError, e:
+            raise ValueError('Bad JSON: %s' % e)
+
+        try:
+            method = json['method']
+            params = json['params']
+            id = json['id']
+            username = json['username'] if 'username' in json else None
+            password = json['password'] if 'password' in json else None
+        except KeyError, e:
+            raise ValueError("JSON body missing parameter: %s" % e)
+
+        if params is None:
+            params = []
+        if not isinstance(params, list):
+            raise ValueError("Bad params %r: must be a list" % params)
+            text = traceback.format_exc()
+            exc_value = sys.exc_info()[1]
+            error_value = dict(
+                name='JSONRPCError',
+                code=100,
+                message=str(exc_value),
+                error=text)
+            return Response(
+                status=500,
+                content_type='application/json',
+                body=dumps(dict(result=None,
+                                error=error_value,
+                                id=id)))
+
+        obj = self.obj
+        if isinstance(self.obj,tuple) or isinstance(self.obj,list):
+            for x in self.obj:
+                if method.startswith('%s.'%x.__class__.__name__):
+                   obj = x
+                   method = method.replace('%s.'%obj.__class__.__name__,'',1)
+                   break
+        elif method.startswith('%s.'%self.obj.__class__.__name__):
+            method = method.replace('%s.'%self.obj.__class__.__name__,'',1)
+        if method.startswith('_'):
+            raise exc.HTTPForbidden("Bad method name %s: must not start with _" % method).exception
+        try:
+            method = getattr(obj, method)
+        except AttributeError:
+            raise ValueError("No such method %s" % method)
+
+        if self.auth is not None:
+            try:
+                auth_result = self.auth(username, password, req.user_agent)
+            except:
+                text = traceback.format_exc()
+                exc_value = sys.exc_info()[1]
+                error_value = dict(
+                    name='JSONRPCError',
+                    code=100,
+                    message=str(exc_value),
+                    error=text)
+                return Response(
+                    status=500,
+                    content_type='application/json',
+                    body=dumps(dict(result=None,
+                                    error=error_value,
+                                    id=id)))
+            if not auth_result:
+                raise exc.HTTPUnauthorized().exception
+
+        try:
+            result = method(*params)
+        except:
+            text = traceback.format_exc()
+            exc_value = sys.exc_info()[1]
+            error_value = dict(
+                name='JSONRPCError',
+                code=100,
+                message=str(exc_value),
+                error=text)
+            return Response(
+                status=500,
+                content_type='application/json',
+                body=dumps(dict(result=None,
+                                error=error_value,
+                                id=id)))
+
+        return Response(
+            content_type='application/json',
+            body=dumps(dict(result=result,
+                            error=None,
+                            id=id)))
+