[Android WebViewShell] Add inclusion test for webview exposed stable interfaces.
[chromium-blink-merge.git] / testing / legion / SimpleJSONRPCServer.py
blob4dbfd6e825c5f542cd4a938ae20f23758a6c725e
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.
5 """Module to implement the SimpleXMLRPCServer module using JSON-RPC.
7 This module uses SimpleXMLRPCServer as the base and only overrides those
8 portions that implement the XML-RPC protocol. These portions are rewritten
9 to use the JSON-RPC protocol instead.
11 When large portions of code need to be rewritten the original code and
12 comments are preserved. The intention here is to keep the amount of code
13 change to a minimum.
15 This module only depends on default Python modules, as well as jsonrpclib
16 which also uses only default modules. No third party code is required to
17 use this module.
18 """
19 import fcntl
20 import json
21 import SimpleXMLRPCServer as _base
22 import SocketServer
23 import sys
24 import traceback
25 try:
26 import gzip
27 except ImportError:
28 gzip = None #python can be built without zlib/gzip support
30 #pylint: disable=relative-import
31 import jsonrpclib
34 class SimpleJSONRPCRequestHandler(_base.SimpleXMLRPCRequestHandler):
35 """Request handler class for received requests.
37 This class extends the functionality of SimpleXMLRPCRequestHandler and only
38 overrides the operations needed to change the protocol from XML-RPC to
39 JSON-RPC.
40 """
42 def do_POST(self):
43 """Handles the HTTP POST request.
45 Attempts to interpret all HTTP POST requests as JSON-RPC calls,
46 which are forwarded to the server's _dispatch method for handling.
47 """
48 # Check that the path is legal
49 if not self.is_rpc_path_valid():
50 self.report_404()
51 return
53 try:
54 # Get arguments by reading body of request.
55 # We read this in chunks to avoid straining
56 # socket.read(); around the 10 or 15Mb mark, some platforms
57 # begin to have problems (bug #792570).
58 max_chunk_size = 10*1024*1024
59 size_remaining = int(self.headers['content-length'])
60 data = []
61 while size_remaining:
62 chunk_size = min(size_remaining, max_chunk_size)
63 chunk = self.rfile.read(chunk_size)
64 if not chunk:
65 break
66 data.append(chunk)
67 size_remaining -= len(data[-1])
68 data = ''.join(data)
69 data = self.decode_request_content(data)
71 if data is None:
72 return # response has been sent
74 # In previous versions of SimpleXMLRPCServer, _dispatch
75 # could be overridden in this class, instead of in
76 # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
77 # check to see if a subclass implements _dispatch and dispatch
78 # using that method if present.
79 response = self.server._marshaled_dispatch(
80 data, getattr(self, '_dispatch', None), self.path)
82 except Exception, e: # This should only happen if the module is buggy
83 # internal error, report as HTTP server error
84 self.send_response(500)
85 # Send information about the exception if requested
86 if (hasattr(self.server, '_send_traceback_header') and
87 self.server._send_traceback_header):
88 self.send_header('X-exception', str(e))
89 self.send_header('X-traceback', traceback.format_exc())
91 self.send_header('Content-length', '0')
92 self.end_headers()
93 else:
94 # got a valid JSON RPC response
95 self.send_response(200)
96 self.send_header('Content-type', 'application/json')
98 if self.encode_threshold is not None:
99 if len(response) > self.encode_threshold:
100 q = self.accept_encodings().get('gzip', 0)
101 if q:
102 try:
103 response = jsonrpclib.gzip_encode(response)
104 self.send_header('Content-Encoding', 'gzip')
105 except NotImplementedError:
106 pass
108 self.send_header('Content-length', str(len(response)))
109 self.end_headers()
110 self.wfile.write(response)
113 class SimpleJSONRPCDispatcher(_base.SimpleXMLRPCDispatcher):
114 """Dispatcher for received JSON-RPC requests.
116 This class extends the functionality of SimpleXMLRPCDispatcher and only
117 overrides the operations needed to change the protocol from XML-RPC to
118 JSON-RPC.
121 def _marshaled_dispatch(self, data, dispatch_method=None, path=None):
122 """Dispatches an JSON-RPC method from marshalled (JSON) data.
124 JSON-RPC methods are dispatched from the marshalled (JSON) data
125 using the _dispatch method and the result is returned as
126 marshalled data. For backwards compatibility, a dispatch
127 function can be provided as an argument (see comment in
128 SimpleJSONRPCRequestHandler.do_POST) but overriding the
129 existing method through subclassing is the preferred means
130 of changing method dispatch behavior.
132 Returns:
133 The JSON-RPC string to return.
135 method = ''
136 params = []
137 ident = ''
138 try:
139 request = json.loads(data)
140 jsonrpclib.ValidateRequest(request)
141 method = request['method']
142 params = request['params']
143 ident = request['id']
145 # generate response
146 if dispatch_method is not None:
147 response = dispatch_method(method, params)
148 else:
149 response = self._dispatch(method, params)
150 response = jsonrpclib.CreateResponseString(response, ident)
152 except jsonrpclib.Fault as fault:
153 response = jsonrpclib.CreateResponseString(fault, ident)
155 # Catch all exceptions here as they should be raised on the caller side.
156 except: #pylint: disable=bare-except
157 # report exception back to server
158 exc_type, exc_value, _ = sys.exc_info()
159 response = jsonrpclib.CreateResponseString(
160 jsonrpclib.Fault(1, '%s:%s' % (exc_type, exc_value)), ident)
161 return response
164 class SimpleJSONRPCServer(SocketServer.TCPServer,
165 SimpleJSONRPCDispatcher):
166 """Simple JSON-RPC server.
168 This class mimics the functionality of SimpleXMLRPCServer and only
169 overrides the operations needed to change the protocol from XML-RPC to
170 JSON-RPC.
173 allow_reuse_address = True
175 # Warning: this is for debugging purposes only! Never set this to True in
176 # production code, as will be sending out sensitive information (exception
177 # and stack trace details) when exceptions are raised inside
178 # SimpleJSONRPCRequestHandler.do_POST
179 _send_traceback_header = False
181 def __init__(self, addr, requestHandler=SimpleJSONRPCRequestHandler,
182 logRequests=True, allow_none=False, encoding=None,
183 bind_and_activate=True):
184 self.logRequests = logRequests
185 SimpleJSONRPCDispatcher.__init__(self, allow_none, encoding)
186 SocketServer.TCPServer.__init__(self, addr, requestHandler,
187 bind_and_activate)
189 # [Bug #1222790] If possible, set close-on-exec flag; if a
190 # method spawns a subprocess, the subprocess shouldn't have
191 # the listening socket open.
192 if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
193 flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
194 flags |= fcntl.FD_CLOEXEC
195 fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)