123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- # Copyright 2014 The Chromium Authors. All rights reserved.
- # Use of this source code is governed by a BSD-style license that can be
- # found in the LICENSE file.
- """Module to implement the SimpleXMLRPCServer module using JSON-RPC.
- This module uses SimpleXMLRPCServer as the base and only overrides those
- portions that implement the XML-RPC protocol. These portions are rewritten
- to use the JSON-RPC protocol instead.
- When large portions of code need to be rewritten the original code and
- comments are preserved. The intention here is to keep the amount of code
- change to a minimum.
- This module only depends on default Python modules, as well as jsonrpclib
- which also uses only default modules. No third party code is required to
- use this module.
- """
- import fcntl
- import json
- import SimpleXMLRPCServer as _base
- import SocketServer
- import sys
- import traceback
- import jsonrpclib
- try:
- import gzip
- except ImportError:
- gzip = None #python can be built without zlib/gzip support
- class SimpleJSONRPCRequestHandler(_base.SimpleXMLRPCRequestHandler):
- """Request handler class for received requests.
- This class extends the functionality of SimpleXMLRPCRequestHandler and only
- overrides the operations needed to change the protocol from XML-RPC to
- JSON-RPC.
- """
- def do_POST(self):
- """Handles the HTTP POST request.
- Attempts to interpret all HTTP POST requests as JSON-RPC calls,
- which are forwarded to the server's _dispatch method for handling.
- """
- # Check that the path is legal
- if not self.is_rpc_path_valid():
- self.report_404()
- return
- try:
- # Get arguments by reading body of request.
- # We read this in chunks to avoid straining
- # socket.read(); around the 10 or 15Mb mark, some platforms
- # begin to have problems (bug #792570).
- max_chunk_size = 10*1024*1024
- size_remaining = int(self.headers['content-length'])
- data = []
- while size_remaining:
- chunk_size = min(size_remaining, max_chunk_size)
- chunk = self.rfile.read(chunk_size)
- if not chunk:
- break
- data.append(chunk)
- size_remaining -= len(data[-1])
- data = ''.join(data)
- data = self.decode_request_content(data)
- if data is None:
- return # response has been sent
- # In previous versions of SimpleXMLRPCServer, _dispatch
- # could be overridden in this class, instead of in
- # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
- # check to see if a subclass implements _dispatch and dispatch
- # using that method if present.
- response = self.server._marshaled_dispatch(
- data, getattr(self, '_dispatch', None), self.path)
- except Exception, e: # This should only happen if the module is buggy
- # internal error, report as HTTP server error
- self.send_response(500)
- # Send information about the exception if requested
- if (hasattr(self.server, '_send_traceback_header') and
- self.server._send_traceback_header):
- self.send_header('X-exception', str(e))
- self.send_header('X-traceback', traceback.format_exc())
- self.send_header('Content-length', '0')
- self.end_headers()
- else:
- # got a valid JSON RPC response
- self.send_response(200)
- self.send_header('Content-type', 'application/json')
- if self.encode_threshold is not None:
- if len(response) > self.encode_threshold:
- q = self.accept_encodings().get('gzip', 0)
- if q:
- try:
- response = jsonrpclib.gzip_encode(response)
- self.send_header('Content-Encoding', 'gzip')
- except NotImplementedError:
- pass
- self.send_header('Content-length', str(len(response)))
- self.end_headers()
- self.wfile.write(response)
- class SimpleJSONRPCDispatcher(_base.SimpleXMLRPCDispatcher):
- """Dispatcher for received JSON-RPC requests.
- This class extends the functionality of SimpleXMLRPCDispatcher and only
- overrides the operations needed to change the protocol from XML-RPC to
- JSON-RPC.
- """
- def _marshaled_dispatch(self, data, dispatch_method=None, path=None):
- """Dispatches an JSON-RPC method from marshalled (JSON) data.
- JSON-RPC methods are dispatched from the marshalled (JSON) data
- using the _dispatch method and the result is returned as
- marshalled data. For backwards compatibility, a dispatch
- function can be provided as an argument (see comment in
- SimpleJSONRPCRequestHandler.do_POST) but overriding the
- existing method through subclassing is the preferred means
- of changing method dispatch behavior.
- Returns:
- The JSON-RPC string to return.
- """
- method = ''
- params = []
- ident = ''
- try:
- request = json.loads(data)
- print 'request:', request
- jsonrpclib.ValidateRequest(request)
- method = request['method']
- params = request['params']
- ident = request['id']
- # generate response
- if dispatch_method is not None:
- response = dispatch_method(method, params)
- else:
- response = self._dispatch(method, params)
- response = jsonrpclib.CreateResponseString(response, ident)
- except jsonrpclib.Fault as fault:
- response = jsonrpclib.CreateResponseString(fault, ident)
- except:
- # report exception back to server
- exc_type, exc_value, _ = sys.exc_info()
- response = jsonrpclib.CreateResponseString(
- jsonrpclib.Fault(1, '%s:%s' % (exc_type, exc_value)), ident)
- print 'response:', response
- return response
- class SimpleJSONRPCServer(SocketServer.TCPServer,
- SimpleJSONRPCDispatcher):
- """Simple JSON-RPC server.
- This class mimics the functionality of SimpleXMLRPCServer and only
- overrides the operations needed to change the protocol from XML-RPC to
- JSON-RPC.
- """
- allow_reuse_address = True
- # Warning: this is for debugging purposes only! Never set this to True in
- # production code, as will be sending out sensitive information (exception
- # and stack trace details) when exceptions are raised inside
- # SimpleJSONRPCRequestHandler.do_POST
- _send_traceback_header = False
- def __init__(self, addr, requestHandler=SimpleJSONRPCRequestHandler,
- logRequests=True, allow_none=False, encoding=None,
- bind_and_activate=True):
- self.logRequests = logRequests
- SimpleJSONRPCDispatcher.__init__(self, allow_none, encoding)
- SocketServer.TCPServer.__init__(self, addr, requestHandler,
- bind_and_activate)
- # [Bug #1222790] If possible, set close-on-exec flag; if a
- # method spawns a subprocess, the subprocess shouldn't have
- # the listening socket open.
- if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
- flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
- flags |= fcntl.FD_CLOEXEC
- fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
|