def __init__(self, uri, key_file=None, cert_file=None, ca_certs=None, validate_cert_hostname=True, extra_headers=None, timeout=None, pool_connections=False, ssl_opts=None): """ :param uri: The endpoint JSON-RPC server URL. :param key_file: (Deprecated) Secret key to use for ssl connection. :param cert_file: (Deprecated) Cert to send to server for ssl connection. :param ca_certs: (Deprecated) File containing concatenated list of certs to validate server cert against. :param extra_headers: Any additional headers to include with all requests. :param pool_connections: Whether to use a thread-local connection pool for connections. :param ssl_opts: Dictionary of options passed to ssl.wrap_socket """ self.logger = logging.getLogger('{0.__module__}.{0.__name__}'.format(self.__class__)) if extra_headers is None: extra_headers = {} parsed_uri = urlparse(uri) self.type = parsed_uri.scheme if self.type not in ("http", "https"): raise JsonRpcError("unsupported JSON-RPC uri: %s" % uri) self.handler = parsed_uri.path port = parsed_uri.port or (80 if self.type == 'http' else 443) self.host = '{}:{}'.format(parsed_uri.hostname, port) if parsed_uri.username and parsed_uri.password: auth = '{}:{}'.format(parsed_uri.username, parsed_uri.password) auth = base64.encodestring(unquote(auth).encode('ascii')) auth = auth.strip() extra_headers.update({"Authorization": b"Basic " + auth}) self.validate_cert_hostname = validate_cert_hostname self.ssl_opts = ssl_opts or {} deprecated_params = {'keyfile': key_file, 'certfile': cert_file, 'ca_certs': ca_certs} for opt, val in deprecated_params.items(): if val is not None: warnings.warn('key_file, cert_file, and ca_certs arguments are deprecated; use ssl_opts argument instead', DeprecationWarning) self.ssl_opts.setdefault(opt, val) # TODO: This could probably be a little cleaner :) if pool_connections: if self.type == "https": self.transport = TLSConnectionPoolSafeTransport(timeout=timeout) else: self.transport = TLSConnectionPoolTransport(timeout=timeout) else: if self.type == "https": self.transport = SafeTransport(timeout=timeout, ssl_opts=self.ssl_opts, validate_cert_hostname=self.validate_cert_hostname) else: self.transport = Transport(timeout=timeout) self.extra_headers = extra_headers self.id = 0 # Initialize our request ID (gets incremented for every request)
def __init__(self, uri, key_file=None, cert_file=None, ca_certs=None, validate_cert_hostname=True, extra_headers=None, timeout=None, pool_connections=False): """ :param uri: The endpoint JSON-RPC server URL. :param key_file: Secret key to use for ssl connection. :param cert_file: Cert to send to server for ssl connection. :param ca_certs: File containing concatenated list of certs to validate server cert against. :param extra_headers: Any additional headers to include with all requests. :param pool_connections: Whether to use a thread-local connection pool for connections. """ self.logger = logging.getLogger('{0.__module__}.{0.__name__}'.format(self.__class__)) if extra_headers is None: extra_headers = {} parsed_uri = urlparse(uri) self.type = parsed_uri.scheme if self.type not in ("http", "https"): raise JsonRpcError("unsupported JSON-RPC uri: %s" % uri) self.handler = parsed_uri.path self.host = parsed_uri.hostname if parsed_uri.username and parsed_uri.password: auth = '{}:{}'.format(parsed_uri.username, parsed_uri.password) auth = base64.encodestring(unquote(auth).encode('ascii')) auth = auth.strip() extra_headers.update({"Authorization": b"Basic " + auth}) self.key_file = key_file self.cert_file = cert_file self.ca_certs = ca_certs self.validate_cert_hostname = validate_cert_hostname # TODO: This could probably be a little cleaner :) if pool_connections: if self.type == "https": self.transport = TLSConnectionPoolSafeTransport(timeout=timeout) else: self.transport = TLSConnectionPoolTransport(timeout=timeout) else: if self.type == "https": self.transport = SafeTransport(key_file=self.key_file, cert_file=self.cert_file, ca_certs=self.ca_certs, validate_cert_hostname=self.validate_cert_hostname) else: self.transport = Transport(timeout=timeout) self.extra_headers = extra_headers self.id = 0 # Initialize our request ID (gets incremented for every request)
class ServerProxy(object): """ The ServerProxy provides a proxy to the remote JSON-RPC service methods and performs the request encoding and decoding. This class uses instance variables to save state and is NOT THREAD-SAFE. :ivar type: The scheme we're using for connection: 'http' or 'https' :type type: C{str} :ivar host: The host we're connecting to (may include port, e.g. "foobar.com:8080") :type host: C{str} :ivar transport: A L{Transport} instance to use. :type transport: L{Transport} :ivar id: The JSON-RPC 'id' field (incrementing integer). :type id: C{int} :ivar extra_headers: Any HTTP request headers that should be sent with every request. :type extra_headers: C{dict} :ivar method_class: The proxy class for the remote methods (default is L{_Method}). :type method_class: C{type} """ method_class = _Method def __init__(self, uri, key_file=None, cert_file=None, ca_certs=None, validate_cert_hostname=True, extra_headers=None, timeout=None, pool_connections=False, ssl_opts=None): """ :param uri: The endpoint JSON-RPC server URL. :param key_file: (Deprecated) Secret key to use for ssl connection. :param cert_file: (Deprecated) Cert to send to server for ssl connection. :param ca_certs: (Deprecated) File containing concatenated list of certs to validate server cert against. :param extra_headers: Any additional headers to include with all requests. :param pool_connections: Whether to use a thread-local connection pool for connections. :param ssl_opts: Dictionary of options passed to ssl.wrap_socket """ self.logger = logging.getLogger('{0.__module__}.{0.__name__}'.format(self.__class__)) if extra_headers is None: extra_headers = {} parsed_uri = urlparse(uri) self.type = parsed_uri.scheme if self.type not in ("http", "https"): raise JsonRpcError("unsupported JSON-RPC uri: %s" % uri) self.handler = parsed_uri.path port = parsed_uri.port or (80 if self.type == 'http' else 443) self.host = '{}:{}'.format(parsed_uri.hostname, port) if parsed_uri.username and parsed_uri.password: auth = '{}:{}'.format(parsed_uri.username, parsed_uri.password) auth = base64.encodestring(unquote(auth).encode('ascii')) auth = auth.strip() extra_headers.update({"Authorization": b"Basic " + auth}) self.validate_cert_hostname = validate_cert_hostname self.ssl_opts = ssl_opts or {} deprecated_params = {'keyfile': key_file, 'certfile': cert_file, 'ca_certs': ca_certs} for opt, val in deprecated_params.items(): if val is not None: warnings.warn('key_file, cert_file, and ca_certs arguments are deprecated; use ssl_opts argument instead', DeprecationWarning) self.ssl_opts.setdefault(opt, val) # TODO: This could probably be a little cleaner :) if pool_connections: if self.type == "https": self.transport = TLSConnectionPoolSafeTransport(timeout=timeout) else: self.transport = TLSConnectionPoolTransport(timeout=timeout) else: if self.type == "https": self.transport = SafeTransport(timeout=timeout, ssl_opts=self.ssl_opts, validate_cert_hostname=self.validate_cert_hostname) else: self.transport = Transport(timeout=timeout) self.extra_headers = extra_headers self.id = 0 # Initialize our request ID (gets incremented for every request) def _request(self, methodname, params): """ Overriden __request method which introduced the request_cookie parameter that allows cookie headers to propagated through JSON-RPC requests. :param methodname: Name of method to be called. :type methodname: C{str} :param params: Parameters list to send to method. :type params: C{list} :return: The decoded result. :raise ResponseError: If the response cannot be parsed or is not proper JSON-RPC 1.0 response format. :raise ProtocolError: Re-raises exception if non-200 response received. :raise Fault: If the response is an error message from remote application. """ self.id += 1 # Increment our "unique" identifier for every request. data = dict(id=self.id, method=methodname, params=params) headers = self.extra_headers self._prepare_request(data, headers) body = json.dumps(data) response = self.transport.request(self.host, self.handler, body, headers=headers) self._handle_response(response) data = response.read() try: decoded = json.loads(data.decode('utf-8')) except Exception as x: raise ResponseError("Unable to parse response data as JSON: %s" % x) # This special casing for non-compliant systems like DD that sometimes # just return NULL from actions and think they're communicating w/ valid # JSON-RPC. if decoded is None: return None if not (('result' in decoded) or ('error' in decoded)): # Include the decoded result (or part of it) in the error we raise r = repr(decoded) if len(r) > 256: # a hard-coded value to keep the exception message to a sane length r = r[0:255] + '...' raise ResponseError('Malformed JSON-RPC response to %s: %s' % (methodname, r)) if 'error' in decoded and decoded['error']: raise Fault(decoded['error']['code'], decoded['error']['message']) if 'result' not in decoded: raise ResponseError('Malformed JSON-RPC response: %r' % decoded) return decoded['result'] def _prepare_request(self, data, headers): """ An extension point hook for preparing the request data before it is encoded and sent to server. This method may modify the body (C{dict}) and headers (C{dict}). Note that some headers (e.g. Content-Length) may be added by the underlying libraries (e.g. httplib). :param data: The request data (C{dict}) that will be sent to server. :type data: C{dict} :param headers: Headers that will be sent with the request. This includes any headers from the extra_headers instance var. :type headers: C{dict} """ pass def _handle_response(self, response): """ An extension point hook for processing the raw response objects from the server. :param response: The HTTP response object. :type response: C{httplib.HTTPResponse} """ pass def __getattr__(self, name): """ Does the proxy magic: returns a proxy callable that will perform request (when called). :return: The callable method object which will perform the request to JSON-RPC server. :rtype: L{_Method} """ return self.method_class(self._request, name) def __repr__(self): return ("<%s for %s%s>" % (self.__class__.__name__, self.host, self.handler))
class ServerProxy(object): """ The ServerProxy provides a proxy to the remote JSON-RPC service methods and performs the request encoding and decoding. This class uses instance variables to save state and is NOT THREAD-SAFE. :ivar type: The scheme we're using for connection: 'http' or 'https' :type type: C{str} :ivar host: The host we're connecting to (may include port, e.g. "foobar.com:8080") :type host: C{str} :ivar transport: A L{Transport} instance to use. :type transport: L{Transport} :ivar id: The JSON-RPC 'id' field (incrementing integer). :type id: C{int} :ivar extra_headers: Any HTTP request headers that should be sent with every request. :type extra_headers: C{dict} :ivar method_class: The proxy class for the remote methods (default is L{_Method}). :type method_class: C{type} """ method_class = _Method def __init__(self, uri, key_file=None, cert_file=None, ca_certs=None, validate_cert_hostname=True, extra_headers=None, timeout=None, pool_connections=False, ssl_opts=None): """ :param uri: The endpoint JSON-RPC server URL. :param key_file: (Deprecated) Secret key to use for ssl connection. :param cert_file: (Deprecated) Cert to send to server for ssl connection. :param ca_certs: (Deprecated) File containing concatenated list of certs to validate server cert against. :param extra_headers: Any additional headers to include with all requests. :param pool_connections: Whether to use a thread-local connection pool for connections. :param ssl_opts: Dictionary of options passed to ssl.wrap_socket """ self.logger = logging.getLogger('{0.__module__}.{0.__name__}'.format( self.__class__)) if extra_headers is None: extra_headers = {} parsed_uri = urlparse(uri) self.type = parsed_uri.scheme if self.type not in ("http", "https"): raise JsonRpcError("unsupported JSON-RPC uri: %s" % uri) self.handler = parsed_uri.path port = parsed_uri.port or (80 if self.type == 'http' else 443) self.host = '{}:{}'.format(parsed_uri.hostname, port) if parsed_uri.username and parsed_uri.password: auth = '{}:{}'.format(parsed_uri.username, parsed_uri.password) auth = base64.encodestring(unquote(auth).encode('ascii')) auth = auth.strip() extra_headers.update({"Authorization": b"Basic " + auth}) self.validate_cert_hostname = validate_cert_hostname self.ssl_opts = ssl_opts or {} deprecated_params = { 'keyfile': key_file, 'certfile': cert_file, 'ca_certs': ca_certs } for opt, val in deprecated_params.items(): if val is not None: warnings.warn( 'key_file, cert_file, and ca_certs arguments are deprecated; use ssl_opts argument instead', DeprecationWarning) self.ssl_opts.setdefault(opt, val) # TODO: This could probably be a little cleaner :) if pool_connections: if self.type == "https": self.transport = TLSConnectionPoolSafeTransport( timeout=timeout) else: self.transport = TLSConnectionPoolTransport(timeout=timeout) else: if self.type == "https": self.transport = SafeTransport( timeout=timeout, ssl_opts=self.ssl_opts, validate_cert_hostname=self.validate_cert_hostname) else: self.transport = Transport(timeout=timeout) self.extra_headers = extra_headers self.id = 0 # Initialize our request ID (gets incremented for every request) def _request(self, methodname, params): """ Overriden __request method which introduced the request_cookie parameter that allows cookie headers to propagated through JSON-RPC requests. :param methodname: Name of method to be called. :type methodname: C{str} :param params: Parameters list to send to method. :type params: C{list} :return: The decoded result. :raise ResponseError: If the response cannot be parsed or is not proper JSON-RPC 1.0 response format. :raise ProtocolError: Re-raises exception if non-200 response received. :raise Fault: If the response is an error message from remote application. """ self.id += 1 # Increment our "unique" identifier for every request. data = dict(id=self.id, method=methodname, params=params) headers = self.extra_headers self._prepare_request(data, headers) body = json.dumps(data) response = self.transport.request(self.host, self.handler, body, headers=headers) self._handle_response(response) data = response.read() try: decoded = json.loads(data.decode('utf-8')) except Exception as x: raise ResponseError("Unable to parse response data as JSON: %s" % x) # This special casing for non-compliant systems like DD that sometimes # just return NULL from actions and think they're communicating w/ valid # JSON-RPC. if decoded is None: return None if not (('result' in decoded) or ('error' in decoded)): # Include the decoded result (or part of it) in the error we raise r = repr(decoded) if len( r ) > 256: # a hard-coded value to keep the exception message to a sane length r = r[0:255] + '...' raise ResponseError('Malformed JSON-RPC response to %s: %s' % (methodname, r)) if 'error' in decoded and decoded['error']: raise Fault(decoded['error']['code'], decoded['error']['message']) if 'result' not in decoded: raise ResponseError('Malformed JSON-RPC response: %r' % decoded) return decoded['result'] def _prepare_request(self, data, headers): """ An extension point hook for preparing the request data before it is encoded and sent to server. This method may modify the body (C{dict}) and headers (C{dict}). Note that some headers (e.g. Content-Length) may be added by the underlying libraries (e.g. httplib). :param data: The request data (C{dict}) that will be sent to server. :type data: C{dict} :param headers: Headers that will be sent with the request. This includes any headers from the extra_headers instance var. :type headers: C{dict} """ pass def _handle_response(self, response): """ An extension point hook for processing the raw response objects from the server. :param response: The HTTP response object. :type response: C{httplib.HTTPResponse} """ pass def __getattr__(self, name): """ Does the proxy magic: returns a proxy callable that will perform request (when called). :return: The callable method object which will perform the request to JSON-RPC server. :rtype: L{_Method} """ return self.method_class(self._request, name) def __repr__(self): return ("<%s for %s%s>" % (self.__class__.__name__, self.host, self.handler))
def __init__(self, uri, key_file=None, cert_file=None, ca_certs=None, validate_cert_hostname=True, extra_headers=None, timeout=None, pool_connections=False, ssl_opts=None): """ :param uri: The endpoint JSON-RPC server URL. :param key_file: (Deprecated) Secret key to use for ssl connection. :param cert_file: (Deprecated) Cert to send to server for ssl connection. :param ca_certs: (Deprecated) File containing concatenated list of certs to validate server cert against. :param extra_headers: Any additional headers to include with all requests. :param pool_connections: Whether to use a thread-local connection pool for connections. :param ssl_opts: Dictionary of options passed to ssl.wrap_socket """ self.logger = logging.getLogger('{0.__module__}.{0.__name__}'.format( self.__class__)) if extra_headers is None: extra_headers = {} parsed_uri = urlparse(uri) self.type = parsed_uri.scheme if self.type not in ("http", "https"): raise JsonRpcError("unsupported JSON-RPC uri: %s" % uri) self.handler = parsed_uri.path port = parsed_uri.port or (80 if self.type == 'http' else 443) self.host = '{}:{}'.format(parsed_uri.hostname, port) if parsed_uri.username and parsed_uri.password: auth = '{}:{}'.format(parsed_uri.username, parsed_uri.password) auth = base64.encodestring(unquote(auth).encode('ascii')) auth = auth.strip() extra_headers.update({"Authorization": b"Basic " + auth}) self.validate_cert_hostname = validate_cert_hostname self.ssl_opts = ssl_opts or {} deprecated_params = { 'keyfile': key_file, 'certfile': cert_file, 'ca_certs': ca_certs } for opt, val in deprecated_params.items(): if val is not None: warnings.warn( 'key_file, cert_file, and ca_certs arguments are deprecated; use ssl_opts argument instead', DeprecationWarning) self.ssl_opts.setdefault(opt, val) # TODO: This could probably be a little cleaner :) if pool_connections: if self.type == "https": self.transport = TLSConnectionPoolSafeTransport( timeout=timeout) else: self.transport = TLSConnectionPoolTransport(timeout=timeout) else: if self.type == "https": self.transport = SafeTransport( timeout=timeout, ssl_opts=self.ssl_opts, validate_cert_hostname=self.validate_cert_hostname) else: self.transport = Transport(timeout=timeout) self.extra_headers = extra_headers self.id = 0 # Initialize our request ID (gets incremented for every request)
def __init__(self, uri, key_file=None, cert_file=None, ca_certs=None, validate_cert_hostname=True, extra_headers=None, timeout=None, pool_connections=False): """ :param uri: The endpoint JSON-RPC server URL. :param key_file: Secret key to use for ssl connection. :param cert_file: Cert to send to server for ssl connection. :param ca_certs: File containing concatenated list of certs to validate server cert against. :param extra_headers: Any additional headers to include with all requests. :param pool_connections: Whether to use a thread-local connection pool for connections. """ self.logger = logging.getLogger('{0.__module__}.{0.__name__}'.format( self.__class__)) if extra_headers is None: extra_headers = {} parsed_uri = urlparse(uri) self.type = parsed_uri.scheme if self.type not in ("http", "https"): raise JsonRpcError("unsupported JSON-RPC uri: %s" % uri) self.handler = parsed_uri.path self.host = parsed_uri.hostname if parsed_uri.username and parsed_uri.password: auth = '{}:{}'.format(parsed_uri.username, parsed_uri.password) auth = base64.encodestring(unquote(auth).encode('ascii')) auth = auth.strip() extra_headers.update({"Authorization": b"Basic " + auth}) self.key_file = key_file self.cert_file = cert_file self.ca_certs = ca_certs self.validate_cert_hostname = validate_cert_hostname # TODO: This could probably be a little cleaner :) if pool_connections: if self.type == "https": self.transport = TLSConnectionPoolSafeTransport( timeout=timeout) else: self.transport = TLSConnectionPoolTransport(timeout=timeout) else: if self.type == "https": self.transport = SafeTransport( key_file=self.key_file, cert_file=self.cert_file, ca_certs=self.ca_certs, validate_cert_hostname=self.validate_cert_hostname) else: self.transport = Transport(timeout=timeout) self.extra_headers = extra_headers self.id = 0 # Initialize our request ID (gets incremented for every request)