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