Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(760)

Side by Side Diff: remoting/tools/remote_test_helper/jsonrpclib.py

Issue 807343002: Adding the first set of remote test cases and associated framework. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: First round of fixes for the remote me2me tests and the associated remote_test_helper code. Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4 """Module to implement the JSON-RPC protocol.
5
6 This module uses xmlrpclib as the base and only overrides those
7 portions that implement the XML-RPC protocol. These portions are rewritten
8 to use the JSON-RPC protocol instead.
9
10 When large portions of code need to be rewritten the original code and
11 comments are preserved. The intention here is to keep the amount of code
12 change to a minimum.
13
14 This module only depends on default Python modules. No third party code is
15 required to use this module.
16 """
17 import json
18 import urllib
19 import xmlrpclib as _base
20
21 gzip_encode = _base.gzip_encode
22
23
24 class Error(Exception):
25
26 def __str__(self):
27 return repr(self)
28
29
30 class ProtocolError(Error):
31 """Indicates a JSON protocol error."""
32
33 def __init__(self, url, errcode, errmsg, headers):
34 Error.__init__(self)
35 self.url = url
36 self.errcode = errcode
37 self.errmsg = errmsg
38 self.headers = headers
39
40 def __repr__(self):
41 return (
42 '<ProtocolError for %s: %s %s>' %
43 (self.url, self.errcode, self.errmsg))
44
45
46 class ResponseError(Error):
47 """Indicates a broken response package."""
48 pass
49
50
51 class Fault(Error):
52 """Indicates an JSON-RPC fault package."""
53
54 def __init__(self, code, message):
55 Error.__init__(self)
56 if not isinstance(code, int):
57 raise ProtocolError('Fault code must be an integer.')
58 self.code = code
59 self.message = message
60
61 def __repr__(self):
62 return (
63 '<Fault %s: %s>' %
64 (self.code, repr(self.message))
65 )
66
67
68 def CreateRequest(methodname, params, ident=''):
69 """Create a valid JSON-RPC request.
70
71 Args:
72 methodname: The name of the remote method to invoke.
73 params: The parameters to pass to the remote method. This should be a
74 list or tuple and able to be encoded by the default JSON parser.
75
76 Returns:
77 A valid JSON-RPC request object.
78 """
79 request = {
80 'jsonrpc': '2.0',
81 'method': methodname,
82 'params': params,
83 'id': ident
84 }
85
86 return request
87
88
89 def CreateRequestString(methodname, params, ident=''):
90 """Create a valid JSON-RPC request string.
91
92 Args:
93 methodname: The name of the remote method to invoke.
94 params: The parameters to pass to the remote method.
95 These parameters need to be encode-able by the default JSON parser.
96 ident: The request identifier.
97
98 Returns:
99 A valid JSON-RPC request string.
100 """
101 return json.dumps(CreateRequest(methodname, params, ident))
102
103 def CreateResponse(data, ident):
104 """Create a JSON-RPC response.
105
106 Args:
107 data: The data to return.
108 ident: The response identifier.
109
110 Returns:
111 A valid JSON-RPC response object.
112 """
113 if isinstance(data, Fault):
114 response = {
115 'jsonrpc': '2.0',
116 'error': {
117 'code': data.code,
118 'message': data.message},
119 'id': ident
120 }
andrewrs 2014/12/18 19:10:32 This trailing brace doesn't look quite aligned in
Mike Meade 2015/01/05 23:18:58 Actually this whole block was badly formatted. Tha
121 else:
122 response = {
123 'jsonrpc': '2.0',
124 'response': data,
125 'id': ident
126 }
127
128 return response
129
130
131 def CreateResponseString(data, ident):
132 """Create a JSON-RPC response string.
133
134 Args:
135 data: The data to return.
136 ident: The response identifier.
137
138 Returns:
139 A valid JSON-RPC response object.
140 """
141 return json.dumps(CreateResponse(data, ident))
142
143
144 def ParseHTTPResponse(response):
145 """Parse an HTTP response object and return the JSON object.
146
147 Args:
148 response: An HTTP response object.
149
150 Returns:
151 The returned JSON-RPC object.
152
153 Raises:
154 ProtocolError: if the object format is not correct.
155 Fault: If a Fault error is returned from the server.
156 """
157 # Check for new http response object, else it is a file object
158 if hasattr(response, 'getheader'):
159 if response.getheader('Content-Encoding', '') == 'gzip':
160 stream = _base.GzipDecodedResponse(response)
161 else:
162 stream = response
163 else:
164 stream = response
165
166 data = ''
167 while 1:
168 chunk = stream.read(1024)
169 if not chunk:
170 break
171 data += chunk
172
173 response = json.loads(data)
174 ValidateBasicJSONRPCData(response)
175
176 if 'response' in response:
177 ValidateResponse(response)
178 return response['response']
179 elif 'error' in response:
180 ValidateError(response)
181 code = response['error']['code']
182 message = response['error']['message']
183 raise Fault(code, message)
184 else:
185 raise ProtocolError('No valid JSON returned')
186
187
188 def ValidateRequest(data):
189 """Validate a JSON-RPC request object.
190
191 Args:
192 data: The JSON-RPC object (dict).
193
194 Raises:
195 ProtocolError: if the object format is not correct.
196 """
197 ValidateBasicJSONRPCData(data)
198 if 'method' not in data or 'params' not in data:
199 raise ProtocolError('JSON is not a valid request')
200
201
202 def ValidateResponse(data):
203 """Validate a JSON-RPC response object.
204
205 Args:
206 data: The JSON-RPC object (dict).
207
208 Raises:
209 ProtocolError: if the object format is not correct.
210 """
211 ValidateBasicJSONRPCData(data)
212 if 'response' not in data:
213 raise ProtocolError('JSON is not a valid response')
214
215
216 def ValidateError(data):
217 """Validate a JSON-RPC error object.
218
219 Args:
220 data: The JSON-RPC object (dict).
221
222 Raises:
223 ProtocolError: if the object format is not correct.
224 """
225 ValidateBasicJSONRPCData(data)
226 if ('error' not in data or
227 'code' not in data['error'] or
228 'message' not in data['error']):
229 raise ProtocolError('JSON is not a valid error response')
230
231
232 def ValidateBasicJSONRPCData(data):
233 """Validate a basic JSON-RPC object.
234
235 Args:
236 data: The JSON-RPC object (dict).
237
238 Raises:
239 ProtocolError: if the object format is not correct.
240 """
241 error = None
242 if not isinstance(data, dict):
243 error = 'JSON data is not a dictionary'
244 elif 'jsonrpc' not in data or data['jsonrpc'] != '2.0':
245 error = 'JSON is not a valid JSON RPC 2.0 message'
246 elif 'id' not in data:
247 error = 'JSON data missing required id entry'
248 if error:
249 raise ProtocolError(error)
250
251
252 class Transport(_base.Transport):
253 """RPC transport class.
254
255 This class extends the functionality of xmlrpclib.Transport and only
256 overrides the operations needed to change the protocol from XML-RPC to
257 JSON-RPC.
258 """
259
andrewrs 2014/12/18 19:10:32 What do you think about setting Transport.user_age
Mike Meade 2015/01/05 23:18:58 Done.
260 def single_request(self, host, handler, request_body, verbose=0):
261 """Issue a single JSON-RPC request."""
262
263 h = self.make_connection(host)
264 if verbose:
265 h.set_debuglevel(1)
266 try:
267 self.send_request(h, handler, request_body)
268 self.send_host(h, host)
269 self.send_user_agent(h)
270 self.send_content(h, request_body)
andrewrs 2014/12/18 19:10:32 Looking at the default implementation of xmlrpclib
Mike Meade 2015/01/05 23:18:58 Good catch on that one. Done.
271
272 response = h.getresponse(buffering=True)
273 if response.status == 200:
274 self.verbose = verbose
275
276 return self.parse_response(response)
277
278 except Fault:
279 raise
280 except Exception:
281 # All unexpected errors leave connection in
282 # a strange state, so we clear it.
283 self.close()
284 raise
285
286 # discard any response data and raise exception
287 if response.getheader('content-length', 0):
288 response.read()
289 raise ProtocolError(
290 host + handler,
291 response.status, response.reason,
292 response.msg,
293 )
294
295 def parse_response(self, response):
296 """Parse the HTTP resoponse from the server."""
297 return ParseHTTPResponse(response)
298
299
300 class SafeTransport(_base.SafeTransport):
301 """Transport class for HTTPS servers.
302
303 This class extends the functionality of xmlrpclib.SafeTransport and only
304 overrides the operations needed to change the protocol from XML-RPC to
305 JSON-RPC.
306 """
307
308 def parse_response(self, response):
309 return ParseHTTPResponse(response)
310
311
312 class ServerProxy(_base.ServerProxy):
313 """Proxy class to the RPC server.
314
315 This class extends the functionality of xmlrpclib.ServerProxy and only
316 overrides the operations needed to change the protocol from XML-RPC to
317 JSON-RPC.
318 """
319
320 def __init__(self, uri, transport=None, encoding=None, verbose=0,
321 allow_none=0, use_datetime=0):
322 urltype, _ = urllib.splittype(uri)
323 if urltype not in ('http', 'https'):
324 raise IOError('unsupported JSON-RPC protocol')
325
326 _base.ServerProxy.__init__(self, uri, transport, encoding, verbose,
327 allow_none, use_datetime)
328
329 if transport is None:
330 if type == 'https':
331 transport = SafeTransport(use_datetime=use_datetime)
332 else:
333 transport = Transport(use_datetime=use_datetime)
334 self.__transport = transport
335
336 def __request(self, methodname, params):
337 """Call a method on the remote server."""
338 request = CreateRequestString(methodname, params)
339
340 response = self.__transport.request(
341 self.__host,
342 self.__handler,
343 request,
344 verbose=self.__verbose
345 )
346
347 return response
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698