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