diff --git a/src/caosdb/connection/connection.py b/src/caosdb/connection/connection.py index e6638d2fd8d55874c557f2cae7c0c58c1995f620..f2c036ab0fb5cb355d0dcfd0448e68936048601d 100644 --- a/src/caosdb/connection/connection.py +++ b/src/caosdb/connection/connection.py @@ -24,26 +24,30 @@ # """Connection to a CaosDB server.""" from __future__ import absolute_import, print_function, unicode_literals + +import logging +import ssl import sys from builtins import str # pylint: disable=redefined-builtin +from errno import EPIPE as BrokenPipe +from socket import error as SocketError + +from caosdb.configuration import get_config +from caosdb.exceptions import (AuthorizationException, CaosDBException, + ClientErrorException, ConfigurationException, + ConnectionException, EntityDoesNotExistError, + LoginFailedException, ServerErrorException, + URITooLongException) + +from .interface import CaosDBHTTPResponse, CaosDBServerConnection +from .streaminghttp import StreamingHTTPSConnection +from .utils import make_uri_path, parse_url, urlencode + try: from urllib.parse import quote, urlparse except ImportError: from urllib import quote from urlparse import urlparse -from errno import EPIPE as BrokenPipe -from socket import error as SocketError -import ssl -import logging -from caosdb.exceptions import (CaosDBException, LoginFailedException, - AuthorizationException, URITooLongException, - ConnectionException, ServerErrorException, - EntityDoesNotExistError, ClientErrorException, - ConfigurationException) -from caosdb.configuration import get_config -from .utils import urlencode, make_uri_path, parse_url -from .interface import CaosDBServerConnection, CaosDBHTTPResponse -from .streaminghttp import StreamingHTTPSConnection # pylint: disable=missing-docstring @@ -106,14 +110,19 @@ class _DefaultCaosDBServerConnection(CaosDBServerConnection): string. **kwargs : Any keyword arguments will be ignored. + TODO: Why are they allowed then? Returns ------- + TODO: What? """ + if headers is None: headers = {} try: self._http_con = StreamingHTTPSConnection( + # TODO looks as if configure needs to be done first. + # That is however not assured. host=self.setup_fields["host"], timeout=self.setup_fields["timeout"], context=self.setup_fields["context"], @@ -124,6 +133,7 @@ class _DefaultCaosDBServerConnection(CaosDBServerConnection): raise ConnectionException( "Connection failed. Network or server down? " + str(socket_err) ) + return _WrappedHTTPResponse(self._http_con.getresponse()) def configure(self, **config): @@ -148,18 +158,22 @@ class _DefaultCaosDBServerConnection(CaosDBServerConnection): If no url has been specified, or if the CA certificate cannot be loaded. """ + if "ssl_version" in config and config["cacert"] is not None: ssl_version = getattr(ssl, config["ssl_version"]) else: ssl_version = ssl.PROTOCOL_TLSv1 context = ssl.SSLContext(ssl_version) context.verify_mode = ssl.CERT_REQUIRED + if config.get("ssl_insecure"): print("Relaxed SSL mode.") context.verify_mode = ssl.CERT_NONE + if (not context.verify_mode == ssl.CERT_NONE and hasattr(context, "check_hostname")): context.check_hostname = True + if ("cacert" in config and config["cacert"] is not None and config["cacert"]): try: @@ -168,6 +182,7 @@ class _DefaultCaosDBServerConnection(CaosDBServerConnection): raise ConnectionException("Could not load the cacert in" "`{}`: {}".format(config["cacert"], exc)) + if "url" in config: parsed_url = parse_url(config["url"]) host = parsed_url.netloc @@ -179,6 +194,7 @@ class _DefaultCaosDBServerConnection(CaosDBServerConnection): "file.") socket_proxy = None + if "socket_proxy" in config: socket_proxy = config["socket_proxy"] @@ -206,14 +222,16 @@ def _make_conf(*conf): A merged config dict. """ result = {} + for conf_dict in conf: result.update(conf_dict) + return result _DEFAULT_CONF = { - "password_method": "plain", - "implementation": _DefaultCaosDBServerConnection, + "password_method": "plain", + "implementation": _DefaultCaosDBServerConnection, "timeout": 210 } @@ -253,6 +271,7 @@ def _get_authenticator(**config): auth_provider = sys.modules[auth_module].get_authentication_provider() auth_provider.configure(**config) + return auth_provider except ImportError: @@ -294,6 +313,7 @@ def configure_connection(**kwargs): connection = _Connection.get_instance() connection.configure(**local_conf) + return connection @@ -304,14 +324,17 @@ def get_connection(): be called inside this function without arguments. """ connection = _Connection.get_instance() + if connection.is_configured: return connection + return configure_connection() def _handle_response_status(http_response): status = http_response.status + if status == 200: return @@ -367,16 +390,19 @@ class _Connection(object): # pylint: disable=useless-object-inheritance def get_instance(cls): if cls.__instance is None: cls.__instance = _Connection() + return cls.__instance def configure(self, **config): self.is_configured = True + if "implementation" not in config: raise ConfigurationException( "Missing CaosDBServerConnection implementation. You did not " "specify an `implementation` for the connection.") try: self._delegate_connection = config["implementation"]() + if not isinstance(self._delegate_connection, CaosDBServerConnection): raise TypeError("The `implementation` callable did not return " @@ -388,12 +414,14 @@ class _Connection(object): # pylint: disable=useless-object-inheritance "instance of `CaosDBServerConnection` (e.g. a constructor " "or a factory).", type_err) self._delegate_connection.configure(**config) + if "password_method" not in config: raise ConfigurationException("Missing password_method. You did " "not specify a `password_method` for" "the connection.") self._authenticator = _get_authenticator( connection=self._delegate_connection, **config) + if "auth_token" in config: self._authenticator.auth_token = config["auth_token"] @@ -403,6 +431,7 @@ class _Connection(object): # pylint: disable=useless-object-inheritance path = make_uri_path(entity_uri_segments, query_dict) http_response = self._http_request(method="GET", path=path, **kwargs) + return http_response def delete(self, entity_uri_segments=None, query_dict=None, **kwargs): @@ -410,12 +439,14 @@ class _Connection(object): # pylint: disable=useless-object-inheritance http_response = self._http_request( method="DELETE", path=path, **kwargs) + return http_response def update(self, entity_uri_segment, query_dict=None, **kwargs): path = make_uri_path(entity_uri_segment, query_dict) http_response = self._http_request(method="PUT", path=path, **kwargs) + return http_response def activate_user(self, link): @@ -425,6 +456,7 @@ class _Connection(object): # pylint: disable=useless-object-inheritance query = fullurl.query http_response = self._http_request( method="GET", path=path + "?" + query) + return http_response def put_form_data(self, entity_uri_segment, params): @@ -446,6 +478,7 @@ class _Connection(object): # pylint: disable=useless-object-inheritance path=quote(path), body=body, headers=headers) + return response def insert(self, entity_uri_segment, query_dict=None, body=None, **kwargs): @@ -453,6 +486,7 @@ class _Connection(object): # pylint: disable=useless-object-inheritance http_response = self._http_request( method="POST", path=path, body=body, **kwargs) + return http_response def download_file(self, path): @@ -461,6 +495,7 @@ class _Connection(object): # pylint: disable=useless-object-inheritance try: uri_segments = ["FileSystem"] uri_segments.extend(path.split("/")) + return self.retrieve(entity_uri_segments=uri_segments) except EntityDoesNotExistError: raise EntityDoesNotExistError("This file does not exist.") @@ -479,6 +514,7 @@ class _Connection(object): # pylint: disable=useless-object-inheritance except SocketError as e: if e.errno != BrokenPipe: raise + return self._retry_http_request(method=method, path=path, headers=headers, body=body, reconnect=False, @@ -486,6 +522,7 @@ class _Connection(object): # pylint: disable=useless-object-inheritance except LoginFailedException: if kwargs.get("reconnect", True) is True: self._login() + return self._retry_http_request(method=method, path=path, headers=headers, body=body, reconnect=False, @@ -497,6 +534,7 @@ class _Connection(object): # pylint: disable=useless-object-inheritance if hasattr(body, "encode"): # python3 body = body.encode("utf-8") + if headers is None: headers = {} self._authenticator.on_request(method=method, path=path, @@ -511,4 +549,5 @@ class _Connection(object): # pylint: disable=useless-object-inheritance str(http_response.getheaders())) _handle_response_status(http_response) self._authenticator.on_response(http_response) + return http_response