OLD | NEW |
---|---|
(Empty) | |
1 # 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.
| |
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 |