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.
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.
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
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
20 import SimpleXMLRPCServer
as _base
28 gzip
= None #python can be built without zlib/gzip support
31 class SimpleJSONRPCRequestHandler(_base
.SimpleXMLRPCRequestHandler
):
32 """Request handler class for received requests.
34 This class extends the functionality of SimpleXMLRPCRequestHandler and only
35 overrides the operations needed to change the protocol from XML-RPC to
40 """Handles the HTTP POST request.
42 Attempts to interpret all HTTP POST requests as JSON-RPC calls,
43 which are forwarded to the server's _dispatch method for handling.
45 # Check that the path is legal
46 if not self
.is_rpc_path_valid():
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'])
59 chunk_size
= min(size_remaining
, max_chunk_size
)
60 chunk
= self
.rfile
.read(chunk_size
)
64 size_remaining
-= len(data
[-1])
66 data
= self
.decode_request_content(data
)
69 return # response has been sent
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
)
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())
88 self
.send_header('Content-length', '0')
91 # got a valid JSON RPC response
92 self
.send_response(200)
93 self
.send_header('Content-type', 'application/json')
95 if self
.encode_threshold
is not None:
96 if len(response
) > self
.encode_threshold
:
97 q
= self
.accept_encodings().get('gzip', 0)
100 response
= jsonrpclib
.gzip_encode(response
)
101 self
.send_header('Content-Encoding', 'gzip')
102 except NotImplementedError:
105 self
.send_header('Content-length', str(len(response
)))
107 self
.wfile
.write(response
)
110 class SimpleJSONRPCDispatcher(_base
.SimpleXMLRPCDispatcher
):
111 """Dispatcher for received JSON-RPC requests.
113 This class extends the functionality of SimpleXMLRPCDispatcher and only
114 overrides the operations needed to change the protocol from XML-RPC to
118 def _marshaled_dispatch(self
, data
, dispatch_method
=None, path
=None):
119 """Dispatches an JSON-RPC method from marshalled (JSON) data.
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.
130 The JSON-RPC string to return.
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']
144 if dispatch_method
is not None:
145 response
= dispatch_method(method
, params
)
147 response
= self
._dispatch
(method
, params
)
148 response
= jsonrpclib
.CreateResponseString(response
, ident
)
150 except jsonrpclib
.Fault
as fault
:
151 response
= jsonrpclib
.CreateResponseString(fault
, ident
)
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
162 class SimpleJSONRPCServer(SocketServer
.TCPServer
,
163 SimpleJSONRPCDispatcher
):
164 """Simple JSON-RPC server.
166 This class mimics the functionality of SimpleXMLRPCServer and only
167 overrides the operations needed to change the protocol from XML-RPC to
171 allow_reuse_address
= True
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
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
,
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
)