Index: remoting/tools/remote_test_helper/SimpleJSONRPCServer.py |
diff --git a/remoting/tools/remote_test_helper/SimpleJSONRPCServer.py b/remoting/tools/remote_test_helper/SimpleJSONRPCServer.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c15c388f8018f4442693b1b8e38481a5223c5624 |
--- /dev/null |
+++ b/remoting/tools/remote_test_helper/SimpleJSONRPCServer.py |
@@ -0,0 +1,193 @@ |
+# Copyright (c) 2014 The Chromium Authors. All rights reserved. |
Jamie
2014/12/17 03:15:41
Nit: No need for (c), here and below.
Mike Meade
2014/12/18 18:54:25
Done.
|
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+"""Module to implement the SimpleXMLRPCServer module using JSON-RPC. |
+ |
+This module uses SimpleXMLRPCServer as the base and only overrides those |
+portions that implement the XML-RPC protocol. These portions are rewritten |
+to use the JSON-RPC protocol instead. |
+ |
+When large portions of code need to be rewritten the original code and |
+comments are preserved. The intention here is to keep the amount of code |
+change to a minimum. |
+ |
+This module only depends on default Python modules, as well as jsonrpclib |
+which also uses only default modules. No third party code is required to |
+use this module. |
+""" |
+import fcntl |
+import json |
+import SimpleXMLRPCServer as _base |
+import SocketServer |
+import sys |
+import traceback |
+import jsonrpclib |
+try: |
+ import gzip |
+except ImportError: |
+ gzip = None #python can be built without zlib/gzip support |
+ |
+ |
+class SimpleJSONRPCRequestHandler(_base.SimpleXMLRPCRequestHandler): |
+ """Request handler class for received requests. |
+ |
+ This class extends the functionality of SimpleXMLRPCRequestHandler and only |
+ overrides the operations needed to change the protocol from XML-RPC to |
+ JSON-RPC. |
+ """ |
+ |
+ def do_POST(self): |
+ """Handles the HTTP POST request. |
+ |
+ Attempts to interpret all HTTP POST requests as JSON-RPC calls, |
+ which are forwarded to the server's _dispatch method for handling. |
+ """ |
+ # Check that the path is legal |
+ if not self.is_rpc_path_valid(): |
+ self.report_404() |
+ return |
+ |
+ try: |
+ # Get arguments by reading body of request. |
+ # We read this in chunks to avoid straining |
+ # socket.read(); around the 10 or 15Mb mark, some platforms |
+ # begin to have problems (bug #792570). |
+ max_chunk_size = 10*1024*1024 |
+ size_remaining = int(self.headers['content-length']) |
+ data = [] |
+ while size_remaining: |
+ chunk_size = min(size_remaining, max_chunk_size) |
+ chunk = self.rfile.read(chunk_size) |
+ if not chunk: |
+ break |
+ data.append(chunk) |
+ size_remaining -= len(data[-1]) |
+ data = ''.join(data) |
+ data = self.decode_request_content(data) |
+ |
+ if data is None: |
+ return # response has been sent |
+ |
+ # In previous versions of SimpleXMLRPCServer, _dispatch |
+ # could be overridden in this class, instead of in |
+ # SimpleXMLRPCDispatcher. To maintain backwards compatibility, |
+ # check to see if a subclass implements _dispatch and dispatch |
+ # using that method if present. |
+ response = self.server._marshaled_dispatch( |
+ data, getattr(self, '_dispatch', None), self.path) |
+ |
+ except Exception, e: # This should only happen if the module is buggy |
+ # internal error, report as HTTP server error |
+ self.send_response(500) |
+ # Send information about the exception if requested |
+ if (hasattr(self.server, '_send_traceback_header') and |
+ self.server._send_traceback_header): |
+ self.send_header('X-exception', str(e)) |
+ self.send_header('X-traceback', traceback.format_exc()) |
+ |
+ self.send_header('Content-length', '0') |
+ self.end_headers() |
+ else: |
+ # got a valid JSON RPC response |
+ self.send_response(200) |
+ self.send_header('Content-type', 'application/json') |
+ |
+ if self.encode_threshold is not None: |
+ if len(response) > self.encode_threshold: |
+ q = self.accept_encodings().get('gzip', 0) |
+ if q: |
+ try: |
+ response = jsonrpclib.gzip_encode(response) |
+ self.send_header('Content-Encoding', 'gzip') |
+ except NotImplementedError: |
+ pass |
+ |
+ self.send_header('Content-length', str(len(response))) |
+ self.end_headers() |
+ self.wfile.write(response) |
+ |
+ |
+class SimpleJSONRPCDispatcher(_base.SimpleXMLRPCDispatcher): |
+ """Dispatcher for received JSON-RPC requests. |
+ |
+ This class extends the functionality of SimpleXMLRPCDispatcher and only |
+ overrides the operations needed to change the protocol from XML-RPC to |
+ JSON-RPC. |
+ """ |
+ |
+ def _marshaled_dispatch(self, data, dispatch_method=None, path=None): |
+ """Dispatches an JSON-RPC method from marshalled (JSON) data. |
+ |
+ JSON-RPC methods are dispatched from the marshalled (JSON) data |
+ using the _dispatch method and the result is returned as |
+ marshalled data. For backwards compatibility, a dispatch |
+ function can be provided as an argument (see comment in |
+ SimpleJSONRPCRequestHandler.do_POST) but overriding the |
+ existing method through subclassing is the preferred means |
+ of changing method dispatch behavior. |
+ |
+ Returns: |
+ The JSON-RPC string to return. |
+ """ |
+ method = '' |
+ params = [] |
+ ident = '' |
+ try: |
+ request = json.loads(data) |
+ print 'request:', request |
+ jsonrpclib.ValidateRequest(request) |
+ method = request['method'] |
+ params = request['params'] |
+ ident = request['id'] |
+ |
+ # generate response |
+ if dispatch_method is not None: |
+ response = dispatch_method(method, params) |
+ else: |
+ response = self._dispatch(method, params) |
+ response = jsonrpclib.CreateResponseString(response, ident) |
+ |
+ except jsonrpclib.Fault as fault: |
+ response = jsonrpclib.CreateResponseString(fault, ident) |
+ |
+ except: |
+ # report exception back to server |
+ exc_type, exc_value, _ = sys.exc_info() |
+ response = jsonrpclib.CreateResponseString( |
+ jsonrpclib.Fault(1, '%s:%s' % (exc_type, exc_value)), ident) |
+ print 'response:', response |
+ return response |
+ |
+ |
+class SimpleJSONRPCServer(SocketServer.TCPServer, |
+ SimpleJSONRPCDispatcher): |
+ """Simple JSON-RPC server. |
+ |
+ This class mimics the functionality of SimpleXMLRPCServer and only |
+ overrides the operations needed to change the protocol from XML-RPC to |
+ JSON-RPC. |
+ """ |
+ |
+ allow_reuse_address = True |
+ |
+ # Warning: this is for debugging purposes only! Never set this to True in |
+ # production code, as will be sending out sensitive information (exception |
+ # and stack trace details) when exceptions are raised inside |
+ # SimpleJSONRPCRequestHandler.do_POST |
+ _send_traceback_header = False |
+ |
+ def __init__(self, addr, requestHandler=SimpleJSONRPCRequestHandler, |
+ logRequests=True, allow_none=False, encoding=None, |
+ bind_and_activate=True): |
+ self.logRequests = logRequests |
+ SimpleJSONRPCDispatcher.__init__(self, allow_none, encoding) |
+ SocketServer.TCPServer.__init__(self, addr, requestHandler, |
+ bind_and_activate) |
+ |
+ # [Bug #1222790] If possible, set close-on-exec flag; if a |
+ # method spawns a subprocess, the subprocess shouldn't have |
+ # the listening socket open. |
+ if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'): |
+ flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD) |
+ flags |= fcntl.FD_CLOEXEC |
+ fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags) |