diff options
author | Dan Klein <danklei@cisco.com> | 2015-08-26 18:16:09 +0300 |
---|---|---|
committer | Dan Klein <danklei@cisco.com> | 2015-08-26 18:16:09 +0300 |
commit | f8ac9d14a989c8cf1535e16165551dfa370b0b74 (patch) | |
tree | 43e396eb5d096ad74ec02afeccf8995a4d241a0f /external_libs/python/jsonrpclib-pelix-0.2.5/jsonrpclib/SimpleJSONRPCServer.py | |
parent | cdcc62972d42f009f55e6aeb2ca5c60c3acd75eb (diff) | |
parent | 53f0e28d7f30c7175cbb15884c309613593859d8 (diff) |
Merge branch 'master' into dan_stateless
Diffstat (limited to 'external_libs/python/jsonrpclib-pelix-0.2.5/jsonrpclib/SimpleJSONRPCServer.py')
-rw-r--r-- | external_libs/python/jsonrpclib-pelix-0.2.5/jsonrpclib/SimpleJSONRPCServer.py | 602 |
1 files changed, 0 insertions, 602 deletions
diff --git a/external_libs/python/jsonrpclib-pelix-0.2.5/jsonrpclib/SimpleJSONRPCServer.py b/external_libs/python/jsonrpclib-pelix-0.2.5/jsonrpclib/SimpleJSONRPCServer.py deleted file mode 100644 index e9fe4e68..00000000 --- a/external_libs/python/jsonrpclib-pelix-0.2.5/jsonrpclib/SimpleJSONRPCServer.py +++ /dev/null @@ -1,602 +0,0 @@ -#!/usr/bin/python -# -- Content-Encoding: UTF-8 -- -""" -Defines a request dispatcher, a HTTP request handler, a HTTP server and a -CGI request handler. - -:authors: Josh Marshall, Thomas Calmant -:copyright: Copyright 2015, isandlaTech -:license: Apache License 2.0 -:version: 0.2.5 - -.. - - Copyright 2015 isandlaTech - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -""" - -# Module version -__version_info__ = (0, 2, 5) -__version__ = ".".join(str(x) for x in __version_info__) - -# Documentation strings format -__docformat__ = "restructuredtext en" - -# ------------------------------------------------------------------------------ -# Local modules -from jsonrpclib import Fault -import jsonrpclib.config -import jsonrpclib.utils as utils -import jsonrpclib.threadpool - -# Standard library -import logging -import socket -import sys -import traceback - -# Prepare the logger -_logger = logging.getLogger(__name__) - -try: - # Python 3 - # pylint: disable=F0401,E0611 - import xmlrpc.server as xmlrpcserver - import socketserver -except (ImportError, AttributeError): - # Python 2 or IronPython - # pylint: disable=F0401,E0611 - import SimpleXMLRPCServer as xmlrpcserver - import SocketServer as socketserver - -try: - # Windows - import fcntl -except ImportError: - # Other systems - # pylint: disable=C0103 - fcntl = None - -# ------------------------------------------------------------------------------ - - -def get_version(request): - """ - Computes the JSON-RPC version - - :param request: A request dictionary - :return: The JSON-RPC version or None - """ - if 'jsonrpc' in request: - return 2.0 - elif 'id' in request: - return 1.0 - - return None - - -def validate_request(request, json_config): - """ - Validates the format of a request dictionary - - :param request: A request dictionary - :param json_config: A JSONRPClib Config instance - :return: True if the dictionary is valid, else a Fault object - """ - if not isinstance(request, utils.DictType): - # Invalid request type - fault = Fault(-32600, 'Request must be a dict, not {0}' - .format(type(request).__name__), - config=json_config) - _logger.warning("Invalid request content: %s", fault) - return fault - - # Get the request ID - rpcid = request.get('id', None) - - # Check request version - version = get_version(request) - if not version: - fault = Fault(-32600, 'Request {0} invalid.'.format(request), - rpcid=rpcid, config=json_config) - _logger.warning("No version in request: %s", fault) - return fault - - # Default parameters: empty list - request.setdefault('params', []) - - # Check parameters - method = request.get('method', None) - params = request.get('params') - param_types = (utils.ListType, utils.DictType, utils.TupleType) - - if not method or not isinstance(method, utils.string_types) or \ - not isinstance(params, param_types): - # Invalid type of method name or parameters - fault = Fault(-32600, 'Invalid request parameters or method.', - rpcid=rpcid, config=json_config) - _logger.warning("Invalid request content: %s", fault) - return fault - - # Valid request - return True - -# ------------------------------------------------------------------------------ - - -class NoMulticallResult(Exception): - """ - No result in multicall - """ - pass - - -class SimpleJSONRPCDispatcher(xmlrpcserver.SimpleXMLRPCDispatcher, object): - """ - Mix-in class that dispatches JSON-RPC requests. - - This class is used to register JSON-RPC method handlers - and then to dispatch them. This class doesn't need to be - instanced directly when used by SimpleJSONRPCServer. - """ - def __init__(self, encoding=None, config=jsonrpclib.config.DEFAULT): - """ - Sets up the dispatcher with the given encoding. - None values are allowed. - """ - xmlrpcserver.SimpleXMLRPCDispatcher.__init__( - self, allow_none=True, encoding=encoding or "UTF-8") - self.json_config = config - - # Notification thread pool - self.__notification_pool = None - - def set_notification_pool(self, thread_pool): - """ - Sets the thread pool to use to handle notifications - """ - self.__notification_pool = thread_pool - - def _unmarshaled_dispatch(self, request, dispatch_method=None): - """ - Loads the request dictionary (unmarshaled), calls the method(s) - accordingly and returns a JSON-RPC dictionary (not marshaled) - - :param request: JSON-RPC request dictionary (or list of) - :param dispatch_method: Custom dispatch method (for method resolution) - :return: A JSON-RPC dictionary (or an array of) or None if the request - was a notification - :raise NoMulticallResult: No result in batch - """ - if not request: - # Invalid request dictionary - fault = Fault(-32600, 'Request invalid -- no request data.', - config=self.json_config) - _logger.warning("Invalid request: %s", fault) - return fault.dump() - - if isinstance(request, utils.ListType): - # This SHOULD be a batch, by spec - responses = [] - for req_entry in request: - # Validate the request - result = validate_request(req_entry, self.json_config) - if isinstance(result, Fault): - responses.append(result.dump()) - continue - - # Call the method - resp_entry = self._marshaled_single_dispatch(req_entry, - dispatch_method) - - # Store its result - if isinstance(resp_entry, Fault): - # pylint: disable=E1103 - responses.append(resp_entry.dump()) - elif resp_entry is not None: - responses.append(resp_entry) - - if not responses: - # No non-None result - _logger.error("No result in Multicall") - raise NoMulticallResult("No result") - - return responses - - else: - # Single call - result = validate_request(request, self.json_config) - if isinstance(result, Fault): - return result.dump() - - # Call the method - response = self._marshaled_single_dispatch(request, - dispatch_method) - if isinstance(response, Fault): - # pylint: disable=E1103 - return response.dump() - - return response - - def _marshaled_dispatch(self, data, dispatch_method=None, path=None): - """ - Parses the request data (marshaled), calls method(s) and returns a - JSON string (marshaled) - - :param data: A JSON request string - :param dispatch_method: Custom dispatch method (for method resolution) - :param path: Unused parameter, to keep compatibility with xmlrpclib - :return: A JSON-RPC response string (marshaled) - """ - # Parse the request - try: - request = jsonrpclib.loads(data, self.json_config) - except Exception as ex: - # Parsing/loading error - fault = Fault(-32700, 'Request {0} invalid. ({1}:{2})' - .format(data, type(ex).__name__, ex), - config=self.json_config) - _logger.warning("Error parsing request: %s", fault) - return fault.response() - - # Get the response dictionary - try: - response = self._unmarshaled_dispatch(request, dispatch_method) - if response is not None: - # Compute the string representation of the dictionary/list - return jsonrpclib.jdumps(response, self.encoding) - else: - # No result (notification) - return '' - except NoMulticallResult: - # Return an empty string (jsonrpclib internal behaviour) - return '' - - def _marshaled_single_dispatch(self, request, dispatch_method=None): - """ - Dispatches a single method call - - :param request: A validated request dictionary - :param dispatch_method: Custom dispatch method (for method resolution) - :return: A JSON-RPC response dictionary, or None if it was a - notification request - """ - method = request.get('method') - params = request.get('params') - - # Prepare a request-specific configuration - if 'jsonrpc' not in request and self.json_config.version >= 2: - # JSON-RPC 1.0 request on a JSON-RPC 2.0 - # => compatibility needed - config = self.json_config.copy() - config.version = 1.0 - else: - # Keep server configuration as is - config = self.json_config - - # Test if this is a notification request - is_notification = 'id' not in request or request['id'] in (None, '') - if is_notification and self.__notification_pool is not None: - # Use the thread pool for notifications - if dispatch_method is not None: - self.__notification_pool.enqueue(dispatch_method, - method, params) - else: - self.__notification_pool.enqueue(self._dispatch, - method, params, config) - - # Return immediately - return None - else: - # Synchronous call - try: - # Call the method - if dispatch_method is not None: - response = dispatch_method(method, params) - else: - response = self._dispatch(method, params, config) - except Exception as ex: - # Return a fault - fault = Fault(-32603, '{0}:{1}'.format(type(ex).__name__, ex), - config=config) - _logger.error("Error calling method %s: %s", method, fault) - return fault.dump() - - if is_notification: - # It's a notification, no result needed - # Do not use 'not id' as it might be the integer 0 - return None - - # Prepare a JSON-RPC dictionary - try: - return jsonrpclib.dump(response, rpcid=request['id'], - is_response=True, config=config) - except Exception as ex: - # JSON conversion exception - fault = Fault(-32603, '{0}:{1}'.format(type(ex).__name__, ex), - config=config) - _logger.error("Error preparing JSON-RPC result: %s", fault) - return fault.dump() - - def _dispatch(self, method, params, config=None): - """ - Default method resolver and caller - - :param method: Name of the method to call - :param params: List of arguments to give to the method - :param config: Request-specific configuration - :return: The result of the method - """ - config = config or self.json_config - - func = None - try: - # Look into registered methods - func = self.funcs[method] - except KeyError: - if self.instance is not None: - # Try with the registered instance - try: - # Instance has a custom dispatcher - return getattr(self.instance, '_dispatch')(method, params) - except AttributeError: - # Resolve the method name in the instance - try: - func = xmlrpcserver.resolve_dotted_attribute( - self.instance, method, True) - except AttributeError: - # Unknown method - pass - - if func is not None: - try: - # Call the method - if isinstance(params, utils.ListType): - return func(*params) - else: - return func(**params) - except TypeError as ex: - # Maybe the parameters are wrong - fault = Fault(-32602, 'Invalid parameters: {0}'.format(ex), - config=config) - _logger.warning("Invalid call parameters: %s", fault) - return fault - except: - # Method exception - err_lines = traceback.format_exc().splitlines() - trace_string = '{0} | {1}'.format(err_lines[-3], err_lines[-1]) - fault = Fault(-32603, 'Server error: {0}'.format(trace_string), - config=config) - _logger.exception("Server-side exception: %s", fault) - return fault - else: - # Unknown method - fault = Fault(-32601, 'Method {0} not supported.'.format(method), - config=config) - _logger.warning("Unknown method: %s", fault) - return fault - -# ------------------------------------------------------------------------------ - - -class SimpleJSONRPCRequestHandler(xmlrpcserver.SimpleXMLRPCRequestHandler): - """ - HTTP request handler. - - The server that receives the requests must have a json_config member, - containing a JSONRPClib Config instance - """ - def do_POST(self): - """ - Handles POST requests - """ - if not self.is_rpc_path_valid(): - self.report_404() - return - - # Retrieve the configuration - config = getattr(self.server, 'json_config', jsonrpclib.config.DEFAULT) - - try: - # Read the request body - max_chunk_size = 10 * 1024 * 1024 - size_remaining = int(self.headers["content-length"]) - chunks = [] - while size_remaining: - chunk_size = min(size_remaining, max_chunk_size) - raw_chunk = self.rfile.read(chunk_size) - if not raw_chunk: - break - chunks.append(utils.from_bytes(raw_chunk)) - size_remaining -= len(chunks[-1]) - data = ''.join(chunks) - - try: - # Decode content - data = self.decode_request_content(data) - if data is None: - # Unknown encoding, response has been sent - return - except AttributeError: - # Available since Python 2.7 - pass - - # Execute the method - response = self.server._marshaled_dispatch( - data, getattr(self, '_dispatch', None), self.path) - - # No exception: send a 200 OK - self.send_response(200) - except: - # Exception: send 500 Server Error - self.send_response(500) - err_lines = traceback.format_exc().splitlines() - trace_string = '{0} | {1}'.format(err_lines[-3], err_lines[-1]) - fault = jsonrpclib.Fault(-32603, 'Server error: {0}' - .format(trace_string), config=config) - _logger.exception("Server-side error: %s", fault) - response = fault.response() - - if response is None: - # Avoid to send None - response = '' - - # Convert the response to the valid string format - response = utils.to_bytes(response) - - # Send it - self.send_header("Content-type", config.content_type) - self.send_header("Content-length", str(len(response))) - self.end_headers() - if response: - self.wfile.write(response) - -# ------------------------------------------------------------------------------ - - -class SimpleJSONRPCServer(socketserver.TCPServer, SimpleJSONRPCDispatcher): - """ - JSON-RPC server (and dispatcher) - """ - # This simplifies server restart after error - allow_reuse_address = True - - # pylint: disable=C0103 - def __init__(self, addr, requestHandler=SimpleJSONRPCRequestHandler, - logRequests=True, encoding=None, bind_and_activate=True, - address_family=socket.AF_INET, - config=jsonrpclib.config.DEFAULT): - """ - Sets up the server and the dispatcher - - :param addr: The server listening address - :param requestHandler: Custom request handler - :param logRequests: Flag to(de)activate requests logging - :param encoding: The dispatcher request encoding - :param bind_and_activate: If True, starts the server immediately - :param address_family: The server listening address family - :param config: A JSONRPClib Config instance - """ - # Set up the dispatcher fields - SimpleJSONRPCDispatcher.__init__(self, encoding, config) - - # Prepare the server configuration - # logRequests is used by SimpleXMLRPCRequestHandler - self.logRequests = logRequests - self.address_family = address_family - self.json_config = config - - # Work on the request handler - class RequestHandlerWrapper(requestHandler, object): - """ - Wraps the request handle to have access to the configuration - """ - def __init__(self, *args, **kwargs): - """ - Constructs the wrapper after having stored the configuration - """ - self.config = config - super(RequestHandlerWrapper, self).__init__(*args, **kwargs) - - # Set up the server - socketserver.TCPServer.__init__(self, addr, requestHandler, - bind_and_activate) - - # Windows-specific - 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) - -# ------------------------------------------------------------------------------ - - -class PooledJSONRPCServer(SimpleJSONRPCServer, socketserver.ThreadingMixIn): - """ - JSON-RPC server based on a thread pool - """ - def __init__(self, addr, requestHandler=SimpleJSONRPCRequestHandler, - logRequests=True, encoding=None, bind_and_activate=True, - address_family=socket.AF_INET, - config=jsonrpclib.config.DEFAULT, thread_pool=None): - """ - Sets up the server and the dispatcher - - :param addr: The server listening address - :param requestHandler: Custom request handler - :param logRequests: Flag to(de)activate requests logging - :param encoding: The dispatcher request encoding - :param bind_and_activate: If True, starts the server immediately - :param address_family: The server listening address family - :param config: A JSONRPClib Config instance - :param thread_pool: A ThreadPool object. The pool must be started. - """ - # Normalize the thread pool - if thread_pool is None: - # Start a thread pool with 30 threads max, 0 thread min - thread_pool = jsonrpclib.threadpool.ThreadPool( - 30, 0, logname="PooledJSONRPCServer") - thread_pool.start() - - # Store the thread pool - self.__request_pool = thread_pool - - # Prepare the server - SimpleJSONRPCServer.__init__(self, addr, requestHandler, logRequests, - encoding, bind_and_activate, - address_family, config) - - def process_request(self, request, client_address): - """ - Handle a client request: queue it in the thread pool - """ - self.__request_pool.enqueue(self.process_request_thread, - request, client_address) - - def server_close(self): - """ - Clean up the server - """ - SimpleJSONRPCServer.server_close(self) - self.__request_pool.stop() - -# ------------------------------------------------------------------------------ - - -class CGIJSONRPCRequestHandler(SimpleJSONRPCDispatcher): - """ - JSON-RPC CGI handler (and dispatcher) - """ - def __init__(self, encoding=None, config=jsonrpclib.config.DEFAULT): - """ - Sets up the dispatcher - - :param encoding: Dispatcher encoding - :param config: A JSONRPClib Config instance - """ - SimpleJSONRPCDispatcher.__init__(self, encoding, config) - - def handle_jsonrpc(self, request_text): - """ - Handle a JSON-RPC request - """ - response = self._marshaled_dispatch(request_text) - sys.stdout.write('Content-Type: {0}\r\n' - .format(self.json_config.content_type)) - sys.stdout.write('Content-Length: {0:d}\r\n'.format(len(response))) - sys.stdout.write('\r\n') - sys.stdout.write(response) - - # XML-RPC alias - handle_xmlrpc = handle_jsonrpc |