annotate 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
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
0
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
1 from webob import Request, Response, exc
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
2 try:
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
3 from json import loads, dumps
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
4 except:
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
5 from simplejson import loads, dumps
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
6 import traceback
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
7 import sys
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
8
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
9 class JsonRpcApp(object):
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
10 """
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
11 Serve the given object via json-rpc (http://json-rpc.org/)
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
12 """
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
13
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
14 def __init__(self, obj, auth=None):
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
15 """
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
16 obj - a class of functions available using jsonrpc
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
17 auth - an authentication function (optional)
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
18 """
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
19 self.obj = obj
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
20 self.auth = auth
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
21
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
22 def __call__(self, environ, start_response):
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
23 req = Request(environ)
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
24 try:
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
25 resp = self.process(req)
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
26 except ValueError, e:
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
27 resp = exc.HTTPBadRequest(str(e))
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
28 except exc.HTTPException, e:
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
29 resp = e
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
30 return resp(environ, start_response)
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
31
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
32 def process(self, req):
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
33 if not req.method == 'POST':
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
34 raise exc.HTTPMethodNotAllowed("Only POST allowed").exception
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
35
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
36 try:
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
37 json = loads(req.body)
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
38 except ValueError, e:
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
39 raise ValueError('Bad JSON: %s' % e)
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
40
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
41 try:
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
42 method = json['method']
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
43 params = json['params']
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
44 id = json['id']
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
45 username = json['username'] if 'username' in json else None
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
46 password = json['password'] if 'password' in json else None
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
47 except KeyError, e:
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
48 raise ValueError("JSON body missing parameter: %s" % e)
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
49
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
50 if params is None:
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
51 params = []
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
52 if not isinstance(params, list):
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
53 raise ValueError("Bad params %r: must be a list" % params)
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
54 text = traceback.format_exc()
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
55 exc_value = sys.exc_info()[1]
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
56 error_value = dict(
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
57 name='JSONRPCError',
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
58 code=100,
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
59 message=str(exc_value),
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
60 error=text)
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
61 return Response(
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
62 status=500,
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
63 content_type='application/json',
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
64 body=dumps(dict(result=None,
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
65 error=error_value,
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
66 id=id)))
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
67
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
68 obj = self.obj
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
69 if isinstance(self.obj,tuple) or isinstance(self.obj,list):
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
70 for x in self.obj:
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
71 if method.startswith('%s.'%x.__class__.__name__):
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
72 obj = x
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
73 method = method.replace('%s.'%obj.__class__.__name__,'',1)
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
74 break
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
75 elif method.startswith('%s.'%self.obj.__class__.__name__):
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
76 method = method.replace('%s.'%self.obj.__class__.__name__,'',1)
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
77 if method.startswith('_'):
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
78 raise exc.HTTPForbidden("Bad method name %s: must not start with _" % method).exception
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
79 try:
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
80 method = getattr(obj, method)
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
81 except AttributeError:
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
82 raise ValueError("No such method %s" % method)
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
83
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
84 if self.auth is not None:
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
85 try:
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
86 auth_result = self.auth(username, password, req.user_agent)
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
87 except:
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
88 text = traceback.format_exc()
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
89 exc_value = sys.exc_info()[1]
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
90 error_value = dict(
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
91 name='JSONRPCError',
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
92 code=100,
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
93 message=str(exc_value),
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
94 error=text)
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
95 return Response(
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
96 status=500,
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
97 content_type='application/json',
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
98 body=dumps(dict(result=None,
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
99 error=error_value,
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
100 id=id)))
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
101 if not auth_result:
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
102 raise exc.HTTPUnauthorized().exception
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
103
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
104 try:
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
105 result = method(*params)
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
106 except:
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
107 text = traceback.format_exc()
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
108 exc_value = sys.exc_info()[1]
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
109 error_value = dict(
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
110 name='JSONRPCError',
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
111 code=100,
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
112 message=str(exc_value),
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
113 error=text)
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
114 return Response(
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
115 status=500,
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
116 content_type='application/json',
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
117 body=dumps(dict(result=None,
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
118 error=error_value,
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
119 id=id)))
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
120
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
121 return Response(
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
122 content_type='application/json',
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
123 body=dumps(dict(result=result,
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
124 error=None,
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
125 id=id)))
6a61cfdf6930 Initial version
Ben Croston <ben@croston.org>
parents:
diff changeset
126