Browse code
Merge pull request #2 from fiddlerwoaroof/master
1 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,26 @@ |
1 |
+from twisted.internet import reactor, ssl |
|
2 |
+from twisted.web import server |
|
3 |
+ |
|
4 |
+from .server import ServerEvents, JSON_RPC |
|
5 |
+ |
|
6 |
+class JSONRPCTest(ServerEvents): |
|
7 |
+ def callmethod(self, request, method, kwargs, args, **kw): |
|
8 |
+ if method in set(['add', 'subtract']): |
|
9 |
+ return getattr(self, method)(*args, **kwargs) |
|
10 |
+ |
|
11 |
+ def subtract(self, a, b): |
|
12 |
+ return a-b |
|
13 |
+ |
|
14 |
+ def add(self, a, b): |
|
15 |
+ return a+b |
|
16 |
+ |
|
17 |
+root = JSON_RPC().customize(JSONRPCTest) |
|
18 |
+site = server.Site(root) |
|
19 |
+ |
|
20 |
+ |
|
21 |
+# 8007 is the port you want to run under. Choose something >1024 |
|
22 |
+reactor.listenTCP(8007, site) |
|
23 |
+reactor.run() |
|
24 |
+ |
|
25 |
+ |
|
26 |
+__version__ = "$Revision: 1.8 $".split(":")[1][:-1].strip() |
... | ... |
@@ -196,14 +196,14 @@ class JSONRPCProxy(object): |
196 | 196 |
return p(*args, **kwargs) |
197 | 197 |
|
198 | 198 |
|
199 |
- def batch_call(self, names, *params): |
|
199 |
+ def batch_call(self, methods): |
|
200 | 200 |
'''call several methods at once, return a list of (result, error) pairs |
201 | 201 |
|
202 |
- :param names: a list of method names |
|
203 |
- :param \\*params: a list of (arg,kwarg) pairs corresponding to each method name |
|
202 |
+ :param names: a dictionary { method: (args, kwargs) } |
|
203 |
+ :returns: a list of pairs (result, error) where only one is not None |
|
204 | 204 |
''' |
205 |
- methods = ( (getattr(self, name),param) for name,param in itertools.izip(names, params) ) |
|
206 |
- data = (method._get_postdata(*params) for method, params in methods) |
|
205 |
+ if hasattr(methods, 'items'): methods = methods.items() |
|
206 |
+ data = [ getattr(self, k)._get_postdata(*v) for k, v in methods ] |
|
207 | 207 |
postdata = '[%s]' % ','.join(data) |
208 | 208 |
respdata = urllib.urlopen(self.serviceURL, postdata).read() |
209 | 209 |
resp = jsonrpc.jsonutil.decode(respdata) |
... | ... |
@@ -1,24 +1,24 @@ |
1 | 1 |
# $Id: server.py,v 1.8 2011/05/26 19:34:19 edwlan Exp $ |
2 | 2 |
|
3 |
-# |
|
3 |
+# |
|
4 | 4 |
# Copyright (c) 2011 Edward Langley |
5 | 5 |
# All rights reserved. |
6 |
-# |
|
6 |
+# |
|
7 | 7 |
# Redistribution and use in source and binary forms, with or without |
8 | 8 |
# modification, are permitted provided that the following conditions |
9 | 9 |
# are met: |
10 |
-# |
|
10 |
+# |
|
11 | 11 |
# Redistributions of source code must retain the above copyright notice, |
12 | 12 |
# this list of conditions and the following disclaimer. |
13 |
-# |
|
13 |
+# |
|
14 | 14 |
# Redistributions in binary form must reproduce the above copyright |
15 | 15 |
# notice, this list of conditions and the following disclaimer in the |
16 | 16 |
# documentation and/or other materials provided with the distribution. |
17 |
-# |
|
17 |
+# |
|
18 | 18 |
# Neither the name of the project's author nor the names of its |
19 | 19 |
# contributors may be used to endorse or promote products derived from |
20 | 20 |
# this software without specific prior written permission. |
21 |
-# |
|
21 |
+# |
|
22 | 22 |
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
23 | 23 |
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
24 | 24 |
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
... | ... |
@@ -30,10 +30,12 @@ |
30 | 30 |
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
31 | 31 |
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
32 | 32 |
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
33 |
-# |
|
33 |
+# |
|
34 | 34 |
# |
35 | 35 |
import cgi |
36 |
+import copy |
|
36 | 37 |
import time |
38 |
+import itertools |
|
37 | 39 |
import jsonrpc.jsonutil |
38 | 40 |
|
39 | 41 |
# Twisted imports |
... | ... |
@@ -60,7 +62,7 @@ class ServerEvents(object): |
60 | 62 |
'''Override to implement custom handling of the method result and request''' |
61 | 63 |
return result |
62 | 64 |
|
63 |
- def logerror(self, result, request): |
|
65 |
+ def log(self, result, request): |
|
64 | 66 |
'''Override to implement custom error handling''' |
65 | 67 |
pass |
66 | 68 |
|
... | ... |
@@ -107,43 +109,34 @@ class JSON_RPC(Resource): |
107 | 109 |
request.write(result) |
108 | 110 |
request.finish() |
109 | 111 |
|
112 |
+ def _ebRender(self, result, request, id, finish=True): |
|
113 |
+ self.eventhandler.log(result, request) |
|
110 | 114 |
|
111 |
- def _ebRender(self, result, request, content, *args, **kwargs): |
|
112 |
- result_template = dict( |
|
113 |
- jsonrpc='2.0', |
|
114 |
- error= dict( |
|
115 |
- code=0, |
|
116 |
- message='Stub Message', |
|
117 |
- data = 'Stub Data' |
|
118 |
- )) |
|
115 |
+ err = None |
|
116 |
+ if not isinstance(result, BaseException): |
|
117 |
+ try: result.raiseException() |
|
118 |
+ except BaseException, e: |
|
119 |
+ err = e |
|
120 |
+ else: err = result |
|
121 |
+ err = self.render_error(err, id) |
|
119 | 122 |
|
120 |
- try: |
|
121 |
- request.setHeader("X-Error", result.getErrorMessage()) |
|
122 |
- result_template['error'].update( |
|
123 |
- code=0, |
|
124 |
- message=' '.join(str(x) for x in result.value), |
|
125 |
- data=content |
|
126 |
- ) |
|
127 |
- if isinstance(content, list): |
|
128 |
- result_template['id'] = content[0].get('id', '<NULL>') |
|
129 |
- result_template = [result] |
|
130 |
- else: result_template['id'] = content.get('id') |
|
131 |
- except Exception, e: |
|
132 |
- result_template['error'].update( |
|
133 |
- code = 0, |
|
134 |
- message = 'Error in errorpage: %s' % e, |
|
135 |
- data = '' |
|
136 |
- ) |
|
137 |
- |
|
138 |
- result = result_template |
|
139 |
- result['error']['message'] = cgi.escape(result['error']['message']) |
|
140 |
- result = jsonrpc.jsonutil.encode(result) |
|
141 |
- result = result.encode('utf-8') |
|
123 |
+ request.setHeader("content-type", 'application/json') |
|
124 |
+ request.setResponseCode(200) |
|
125 |
+ result = jsonrpc.jsonutil.encode(err).encode('utf-8') |
|
142 | 126 |
request.setHeader("content-length", len(result)) |
143 |
- request.setResponseCode(500) |
|
144 |
- self.eventhandler.logerror(result, request) |
|
145 | 127 |
request.write(result) |
146 |
- request.finish() |
|
128 |
+ if finish: request.finish() |
|
129 |
+ |
|
130 |
+ |
|
131 |
+ def get_result(method_result, content, template): |
|
132 |
+ template['error'].update( |
|
133 |
+ code=0, |
|
134 |
+ message=cgi.escape(' '.join(str(x) for x in method_result.value)), |
|
135 |
+ data=content |
|
136 |
+ ) |
|
137 |
+ template['id'] = content.get(id) |
|
138 |
+ return template |
|
139 |
+ |
|
147 | 140 |
|
148 | 141 |
|
149 | 142 |
def render(self, request): |
... | ... |
@@ -151,12 +144,28 @@ class JSON_RPC(Resource): |
151 | 144 |
host = request.getClientIP() |
152 | 145 |
|
153 | 146 |
request.content.seek(0, 0) |
154 |
- content = jsonrpc.jsonutil.decode(request.content.read()) |
|
155 |
- d = threads.deferToThread(self._action, request, content, ctxid=ctxid, host=host) |
|
156 |
- d.addCallback(self._cbRender, request) |
|
157 |
- d.addErrback(self._ebRender, request, content) |
|
147 |
+ try: |
|
148 |
+ content = jsonrpc.jsonutil.decode(request.content.read()) |
|
149 |
+ d = threads.deferToThread(self._action, request, content, ctxid=ctxid, host=host) |
|
150 |
+ d.addCallback(self._cbRender, request) |
|
151 |
+ d.addErrback(self._ebRender, request, content.get('id') if hasattr(content, 'get') else None) |
|
152 |
+ except BaseException, e: |
|
153 |
+ self._ebRender(e, request, None) |
|
154 |
+ |
|
158 | 155 |
return server.NOT_DONE_YET |
159 | 156 |
|
157 |
+ def render_error(self, e, id): |
|
158 |
+ err = dict( |
|
159 |
+ jsonrpc='2.0', |
|
160 |
+ id = id, |
|
161 |
+ error= dict( |
|
162 |
+ code=0, |
|
163 |
+ message=str(e), |
|
164 |
+ data = e.args |
|
165 |
+ )) |
|
166 |
+ return err |
|
167 |
+ |
|
168 |
+ |
|
160 | 169 |
|
161 | 170 |
def _action(self, request, contents, **kw): |
162 | 171 |
result = [] |
... | ... |
@@ -165,19 +174,23 @@ class JSON_RPC(Resource): |
165 | 174 |
contents = [contents] |
166 | 175 |
|
167 | 176 |
for content in contents: |
168 |
- res = dict( |
|
169 |
- jsonrpc = '2.0', |
|
170 |
- id = content.get('id'), |
|
171 |
- result = self.eventhandler.callmethod(request, *self._parse_data(content), **kw) |
|
172 |
- ) |
|
177 |
+ try: |
|
178 |
+ res = dict( |
|
179 |
+ jsonrpc = '2.0', |
|
180 |
+ id = content.get('id'), |
|
181 |
+ result = self.eventhandler.callmethod(request, *self._parse_data(content), **kw) |
|
182 |
+ ) |
|
173 | 183 |
|
174 |
- res = self.eventhandler.processrequest(res, request.args) |
|
184 |
+ res = self.eventhandler.processrequest(res, request.args) |
|
175 | 185 |
|
176 |
- result.append(res) |
|
186 |
+ result.append(res) |
|
187 |
+ except Exception, e: |
|
188 |
+ err = self.render_error(e, content.get('id')) |
|
189 |
+ result.append(err) |
|
177 | 190 |
|
178 | 191 |
|
179 |
- return ( result if ol else result[0] ) |
|
192 |
+ self.eventhandler.log(result, request) |
|
180 | 193 |
|
194 |
+ return ( result if ol else result[0] ) |
|
181 | 195 |
|
182 | 196 |
|
183 |
-__version__ = "$Revision: 1.8 $".split(":")[1][:-1].strip() |