Ejemplo n.º 1
0
def _handshake(s, resolved_address):
    """

    :param s:
    :return:
    """
    local_port = s.getsockname()[1]

    # Send details of the protocol versions supported
    supported_versions = [3, 0, 0, 0]
    handshake = [Bolt.MAGIC_PREAMBLE] + supported_versions
    log.debug("[#%04X]  C: <MAGIC> 0x%08X", local_port, Bolt.MAGIC_PREAMBLE)
    log.debug("[#%04X]  C: <HANDSHAKE> 0x%08X 0x%08X 0x%08X 0x%08X",
              local_port, *supported_versions)
    data = b"".join(struct_pack(">I", num) for num in handshake)
    s.sendall(data)

    # Handle the handshake response
    ready_to_read = False
    while not ready_to_read:
        ready_to_read, _, _ = select((s, ), (), (), 1)
    try:
        data = s.recv(4)
    except OSError:
        raise ServiceUnavailable("Failed to read any data from server {!r} "
                                 "after connected".format(resolved_address))
    data_size = len(data)
    if data_size == 0:
        # If no data is returned after a successful select
        # response, the server has closed the connection
        log.debug("[#%04X]  S: <CLOSE>", local_port)
        s.close()
        raise ServiceUnavailable("Connection to %r closed without handshake "
                                 "response" % (resolved_address, ))
    if data_size != 4:
        # Some garbled data has been received
        log.debug("[#%04X]  S: @*#!", local_port)
        s.close()
        raise ProtocolError("Expected four byte Bolt handshake response "
                            "from %r, received %r instead; check for "
                            "incorrect port number" % (resolved_address, data))
    elif data == b"HTTP":
        log.debug("[#%04X]  S: <CLOSE>", local_port)
        s.close()
        raise ServiceUnavailable("Cannot to connect to Bolt service on {!r} "
                                 "(looks like HTTP)".format(resolved_address))
    agreed_version = data[-1], data[-2]
    log.debug("[#%04X]  S: <HANDSHAKE> 0x%06X%02X", local_port,
              agreed_version[1], agreed_version[0])
    if agreed_version == (0, 0):
        log.debug("[#%04X]  C: <CLOSE>", local_port)
        s.shutdown(SHUT_RDWR)
        s.close()
    elif agreed_version in ((3, 0), ):
        return s, agreed_version
    else:
        log.debug("[#%04X]  S: <CLOSE>", local_port)
        s.close()
        raise ProtocolError("Unknown Bolt protocol version: "
                            "{}".format(agreed_version))
Ejemplo n.º 2
0
def _connect(resolved_address, **config):
    """

    :param resolved_address:
    :param config:
    :return: socket object
    """
    s = None
    try:
        if len(resolved_address) == 2:
            s = socket(AF_INET)
        elif len(resolved_address) == 4:
            s = socket(AF_INET6)
        else:
            raise ValueError("Unsupported address {!r}".format(resolved_address))
        t = s.gettimeout()
        s.settimeout(config.get("connection_timeout", default_config["connection_timeout"]))
        log_debug("~~ [CONNECT] %s", resolved_address)
        s.connect(resolved_address)
        s.settimeout(t)
        s.setsockopt(SOL_SOCKET, SO_KEEPALIVE, 1 if config.get("keep_alive", default_config["keep_alive"]) else 0)
    except SocketTimeout:
        _force_close(s)
        raise ServiceUnavailable("Timed out trying to establish connection to {!r}".format(resolved_address))
    except SocketError as error:
        _force_close(s)
        if error.errno in (61, 99, 111, 10061):
            raise ServiceUnavailable("Failed to establish connection to {!r} (reason {})".format(resolved_address, error.errno))
        else:
            raise
    except ConnectionResetError:
        raise ServiceUnavailable("Failed to establish connection to {!r}".format(resolved_address))
    else:
        return s
    def _set_defunct(self, message, error=None):
        direct_driver = isinstance(self.pool, BoltPool)

        if error:
            log.error(str(error))
        log.error(message)
        # We were attempting to receive data but the connection
        # has unexpectedly terminated. So, we need to close the
        # connection from the client side, and remove the address
        # from the connection pool.
        self._defunct = True
        self.close()
        if self.pool:
            self.pool.deactivate(address=self.unresolved_address)
        # Iterate through the outstanding responses, and if any correspond
        # to COMMIT requests then raise an error to signal that we are
        # unable to confirm that the COMMIT completed successfully.
        for response in self.responses:
            if isinstance(response, CommitResponse):
                if error:
                    raise IncompleteCommit(message) from error
                else:
                    raise IncompleteCommit(message)

        if direct_driver:
            if error:
                raise ServiceUnavailable(message) from error
            else:
                raise ServiceUnavailable(message)
        else:
            if error:
                raise SessionExpired(message) from error
            else:
                raise SessionExpired(message)
    def fetch_message(self):
        """ Receive at least one message from the server, if available.

        :return: 2-tuple of number of detail messages and number of summary
                 messages fetched
        """
        if self._closed:
            raise ServiceUnavailable(
                "Failed to read from closed connection {!r} ({!r})".format(
                    self.unresolved_address, self.server_info.address))

        if self._defunct:
            raise ServiceUnavailable(
                "Failed to read from defunct connection {!r} ({!r})".format(
                    self.unresolved_address, self.server_info.address))

        if not self.responses:
            return 0, 0

        # Receive exactly one message
        details, summary_signature, summary_metadata = next(self.inbox)

        if details:
            log.debug("[#%04X]  S: RECORD * %d", self.local_port,
                      len(details))  # Do not log any data
            self.responses[0].on_records(details)

        if summary_signature is None:
            return len(details), 0

        response = self.responses.popleft()
        response.complete = True
        if summary_signature == b"\x70":
            log.debug("[#%04X]  S: SUCCESS %r", self.local_port,
                      summary_metadata)
            response.on_success(summary_metadata or {})
        elif summary_signature == b"\x7E":
            log.debug("[#%04X]  S: IGNORED", self.local_port)
            response.on_ignored(summary_metadata or {})
        elif summary_signature == b"\x7F":
            log.debug("[#%04X]  S: FAILURE %r", self.local_port,
                      summary_metadata)
            try:
                response.on_failure(summary_metadata or {})
            except (ServiceUnavailable, DatabaseUnavailable):
                if self.pool:
                    self.pool.deactivate(address=self.unresolved_address),
                raise
            except (NotALeader, ForbiddenOnReadOnlyDatabase):
                if self.pool:
                    self.pool.on_write_failure(
                        address=self.unresolved_address),
                raise
        else:
            raise BoltProtocolError(
                "Unexpected response message with signature %02X" %
                summary_signature,
                address=self.unresolved_address)

        return len(details), 1
Ejemplo n.º 5
0
def _handshake(s, resolved_address, der_encoded_server_certificate, error_handler, **config):
    """

    :param s:
    :return:
    """

    # Send details of the protocol versions supported
    supported_versions = [2, 1, 0, 0]
    handshake = [MAGIC_PREAMBLE] + supported_versions
    log_debug("C: [HANDSHAKE] 0x%X %r", MAGIC_PREAMBLE, supported_versions)
    data = b"".join(struct_pack(">I", num) for num in handshake)
    s.sendall(data)

    # Handle the handshake response
    ready_to_read, _, _ = select((s,), (), (), 0)
    while not ready_to_read:
        ready_to_read, _, _ = select((s,), (), (), 0)
    try:
        data = s.recv(4)
    except ConnectionResetError:
        raise ServiceUnavailable("Failed to read any data from server {!r} after connected".format(resolved_address))
    data_size = len(data)
    if data_size == 0:
        # If no data is returned after a successful select
        # response, the server has closed the connection
        log_error("S: [CLOSE]")
        s.close()
        raise ProtocolError("Connection to %r closed without handshake response" % (resolved_address,))
    if data_size != 4:
        # Some garbled data has been received
        log_error("S: @*#!")
        s.close()
        raise ProtocolError("Expected four byte handshake response, received %r instead" % data)
    agreed_version, = struct_unpack(">I", data)
    log_debug("S: [HANDSHAKE] %d", agreed_version)
    if agreed_version == 0:
        log_debug("~~ [CLOSE]")
        s.shutdown(SHUT_RDWR)
        s.close()
    elif agreed_version in (1, 2):
        connection = Connection(resolved_address, s, agreed_version,
                                der_encoded_server_certificate=der_encoded_server_certificate,
                                error_handler=error_handler, **config)
        connection.init()
        return connection
    elif agreed_version == 0x48545450:
        log_error("S: [CLOSE]")
        s.close()
        raise ServiceUnavailable("Cannot to connect to Bolt service on {!r} "
                                 "(looks like HTTP)".format(resolved_address))
    else:
        log_error("S: [CLOSE]")
        s.close()
        raise ProtocolError("Unknown Bolt protocol version: {}".format(agreed_version))
Ejemplo n.º 6
0
def _handshake(s, resolved_address):
    """

    :param s: Socket
    :param resolved_address:

    :return: (socket, version, client_handshake, server_response_data)
    """
    local_port = s.getsockname()[1]

    # TODO: Optimize logging code
    handshake = Bolt.get_handshake()
    import struct
    handshake = struct.unpack(">16B", handshake)
    handshake = [handshake[i:i + 4] for i in range(0, len(handshake), 4)]

    supported_versions = [("0x%02X%02X%02X%02X" % (vx[0], vx[1], vx[2], vx[3])) for vx in handshake]

    log.debug("[#%04X]  C: <MAGIC> 0x%08X", local_port, int.from_bytes(Bolt.MAGIC_PREAMBLE, byteorder="big"))
    log.debug("[#%04X]  C: <HANDSHAKE> %s %s %s %s", local_port, *supported_versions)

    data = Bolt.MAGIC_PREAMBLE + Bolt.get_handshake()
    s.sendall(data)

    # Handle the handshake response
    ready_to_read = False
    while not ready_to_read:
        ready_to_read, _, _ = select((s,), (), (), 1)
    try:
        data = s.recv(4)
    except OSError:
        raise ServiceUnavailable("Failed to read any data from server {!r} "
                                 "after connected".format(resolved_address))
    data_size = len(data)
    if data_size == 0:
        # If no data is returned after a successful select
        # response, the server has closed the connection
        log.debug("[#%04X]  S: <CLOSE>", local_port)
        s.close()
        raise BoltHandshakeError("Connection to {address} closed without handshake response".format(address=resolved_address), address=resolved_address, request_data=handshake, response_data=None)
    if data_size != 4:
        # Some garbled data has been received
        log.debug("[#%04X]  S: @*#!", local_port)
        s.close()
        raise BoltProtocolError("Expected four byte Bolt handshake response from %r, received %r instead; check for incorrect port number" % (resolved_address, data), address=resolved_address)
    elif data == b"HTTP":
        log.debug("[#%04X]  S: <CLOSE>", local_port)
        s.close()
        raise ServiceUnavailable("Cannot to connect to Bolt service on {!r} "
                                 "(looks like HTTP)".format(resolved_address))
    agreed_version = data[-1], data[-2]
    log.debug("[#%04X]  S: <HANDSHAKE> 0x%06X%02X", local_port, agreed_version[1], agreed_version[0])
    return s, agreed_version, handshake, data
    def send_all(self):
        """ Send all queued messages to the server.
        """
        if self.closed():
            raise ServiceUnavailable(
                "Failed to write to closed connection {!r} ({!r})".format(
                    self.unresolved_address, self.server_info.address))

        if self.defunct():
            raise ServiceUnavailable(
                "Failed to write to defunct connection {!r} ({!r})".format(
                    self.unresolved_address, self.server_info.address))

        self._send_all()
Ejemplo n.º 8
0
    def acquire_direct(self, address):
        """ Acquire a connection to a given address from the pool.
        The address supplied should always be an IP address, not
        a host name.

        This method is thread safe.
        """
        if self.closed():
            raise ServiceUnavailable("Connection pool closed")
        if not is_ip_address(address[0]):
            raise ValueError("Invalid IP address {!r}".format(address[0]))
        with self.lock:
            try:
                connections = self.connections[address]
            except KeyError:
                connections = self.connections[address] = deque()
            for connection in list(connections):
                if connection.closed() or connection.defunct():
                    connections.remove(connection)
                    continue
                if not connection.in_use:
                    connection.in_use = True
                    return connection
            try:
                connection = self.connector(address)
            except ServiceUnavailable:
                self.remove(address)
                raise
            else:
                connection.pool = self
                connection.in_use = True
                connections.append(connection)
                return connection
Ejemplo n.º 9
0
def connect(address, *, timeout, custom_resolver, ssl_context, keep_alive):
    """ Connect and perform a handshake and return a valid Connection object,
    assuming a protocol version can be agreed.
    """
    last_error = None
    # Establish a connection to the host and port specified
    # Catches refused connections see:
    # https://docs.python.org/2/library/errno.html
    log.debug("[#0000]  C: <RESOLVE> %s", address)

    for resolved_address in Address(address).resolve(resolver=custom_resolver):
        s = None
        try:
            host = address[0]
            s = _connect(resolved_address, timeout, keep_alive)
            s = _secure(s, host, ssl_context)
            return _handshake(s, address)
        except Exception as error:
            if s:
                s.close()
            last_error = error
    if last_error is None:
        raise ServiceUnavailable("Failed to resolve addresses for %s" % address)
    else:
        raise last_error
Ejemplo n.º 10
0
    def fetch_routing_info(self, address):
        """ Fetch raw routing info from a given router address.

        :param address: router address
        :return: list of routing records or
                 None if no connection could be established
        :raise ServiceUnavailable: if the server does not support routing or
                                   if routing support is broken
        """
        metadata = {}
        records = []

        def fail(md):
            if md.get("code") == "Neo.ClientError.Procedure.ProcedureNotFound":
                raise RoutingProtocolError("Server {!r} does not support routing".format(address))
            else:
                raise RoutingProtocolError("Routing support broken on server {!r}".format(address))

        try:
            with self.acquire_direct(address) as cx:
                _, _, server_version = (cx.server.agent or "").partition("/")
                log.debug("[#%04X]  C: <ROUTING> query=%r", cx.local_port, self.routing_context or {})
                cx.run("CALL dbms.cluster.routing.getRoutingTable({context})",
                       {"context": self.routing_context}, on_success=metadata.update, on_failure=fail)
                cx.pull_all(on_success=metadata.update, on_records=records.extend)
                cx.send_all()
                cx.fetch_all()
                routing_info = [dict(zip(metadata.get("fields", ()), values)) for values in records]
                log.debug("[#%04X]  S: <ROUTING> info=%r", cx.local_port, routing_info)
            return routing_info
        except RoutingProtocolError as error:
            raise ServiceUnavailable(*error.args)
        except ServiceUnavailable:
            self.deactivate(address)
            return None
Ejemplo n.º 11
0
 def on_failure(self, metadata):
     code = metadata.get("code")
     message = metadata.get("message", "Connection initialisation failed")
     if code == "Neo.ClientError.Security.Unauthorized":
         raise AuthError(message)
     else:
         raise ServiceUnavailable(message)
Ejemplo n.º 12
0
def connect(address, **config):
    """ Connect and perform a handshake and return a valid Connection object,
    assuming a protocol version can be agreed.
    """
    ssl_context = make_ssl_context(**config)
    last_error = None
    # Establish a connection to the host and port specified
    # Catches refused connections see:
    # https://docs.python.org/2/library/errno.html
    log.debug("[#0000]  C: <RESOLVE> %s", address)
    address_list = AddressList([address])
    address_list.custom_resolve(config.get("resolver"))
    address_list.dns_resolve()
    for resolved_address in address_list:
        s = None
        try:
            host = address[0]
            s = _connect(resolved_address, **config)
            s, der_encoded_server_certificate = _secure(s, host, ssl_context)
            connection = _handshake(s, address, der_encoded_server_certificate,
                                    **config)
        except Exception as error:
            if s:
                s.close()
            last_error = error
        else:
            return connection
    if last_error is None:
        raise ServiceUnavailable("Failed to resolve addresses for %s" %
                                 address)
    else:
        raise last_error
Ejemplo n.º 13
0
    def update_routing_table(self, *, database):
        """ Update the routing table from the first router able to provide
        valid routing information.

        :param database: The database name

        :raise neo4j.exceptions.ServiceUnavailable:
        """
        # copied because it can be modified
        existing_routers = list(self.routing_tables[database].routers)

        has_tried_initial_routers = False
        if self.routing_tables[database].missing_fresh_writer():
            # TODO: Test this state
            has_tried_initial_routers = True
            if self.update_routing_table_from(self.first_initial_routing_address, database=database):
                # Why is only the first initial routing address used?
                return

        if self.update_routing_table_from(*existing_routers, database=database):
            return

        if not has_tried_initial_routers and self.first_initial_routing_address not in existing_routers:
            if self.update_routing_table_from(self.first_initial_routing_address, database=database):
                # Why is only the first initial routing address used?
                return

        # None of the routers have been successful, so just fail
        log.error("Unable to retrieve routing information")
        raise ServiceUnavailable("Unable to retrieve routing information")
Ejemplo n.º 14
0
def connect(address, ssl_context=None, error_handler=None, **config):
    """ Connect and perform a handshake and return a valid Connection object, assuming
    a protocol version can be agreed.
    """

    last_error = None
    # Establish a connection to the host and port specified
    # Catches refused connections see:
    # https://docs.python.org/2/library/errno.html
    log_debug("~~ [RESOLVE] %s", address)
    resolver = Resolver(custom_resolver=config.get("resolver"))
    resolver.addresses.append(address)
    resolver.custom_resolve()
    resolver.dns_resolve()
    for resolved_address in resolver.addresses:
        log_debug("~~ [RESOLVED] %s -> %s", address, resolved_address)
        try:
            s = _connect(resolved_address, **config)
            s, der_encoded_server_certificate = _secure(
                s, address[0], ssl_context, **config)
            connection = _handshake(s, resolved_address,
                                    der_encoded_server_certificate,
                                    error_handler, **config)
        except Exception as error:
            last_error = error
        else:
            return connection
    if last_error is None:
        raise ServiceUnavailable("Failed to resolve addresses for %s" %
                                 address)
    else:
        raise last_error
Ejemplo n.º 15
0
    def acquire_direct(self, address):
        """ Acquire a connection to a given address from the pool.
        The address supplied should always be an IP address, not
        a host name.

        This method is thread safe.
        """
        if self.closed():
            raise ServiceUnavailable("Connection pool closed")
        if not is_ip_address(address[0]):
            raise ValueError("Invalid IP address {!r}".format(address[0]))
        with self.lock:
            try:
                connections = self.connections[address]
            except KeyError:
                connections = self.connections[address] = deque()

            connection_acquisition_start_timestamp = clock()
            while True:
                # try to find a free connection in pool
                for connection in list(connections):
                    if connection.closed() or connection.defunct(
                    ) or connection.timedout():
                        connections.remove(connection)
                        continue
                    if not connection.in_use:
                        connection.in_use = True
                        return connection
                # all connections in pool are in-use
                can_create_new_connection = self._max_connection_pool_size == INFINITE or len(
                    connections) < self._max_connection_pool_size
                if can_create_new_connection:
                    try:
                        connection = self.connector(
                            address, self.connection_error_handler)
                    except ServiceUnavailable:
                        self.remove(address)
                        raise
                    else:
                        connection.pool = self
                        connection.in_use = True
                        connections.append(connection)
                        return connection

                # failed to obtain a connection from pool because the pool is full and no free connection in the pool
                span_timeout = self._connection_acquisition_timeout - (
                    clock() - connection_acquisition_start_timestamp)
                if span_timeout > 0:
                    self.cond.wait(span_timeout)
                    # if timed out, then we throw error. This time computation is needed, as with python 2.7, we cannot
                    # tell if the condition is notified or timed out when we come to this line
                    if self._connection_acquisition_timeout <= (
                            clock() - connection_acquisition_start_timestamp):
                        raise ClientError(
                            "Failed to obtain a connection from pool within {!r}s"
                            .format(self._connection_acquisition_timeout))
                else:
                    raise ClientError(
                        "Failed to obtain a connection from pool within {!r}s".
                        format(self._connection_acquisition_timeout))
Ejemplo n.º 16
0
    def _run_transaction(self, access_mode, transaction_function, *args,
                         **kwargs):

        if not callable(transaction_function):
            raise TypeError("Unit of work is not callable")

        metadata = getattr(transaction_function, "metadata", None)
        timeout = getattr(transaction_function, "timeout", None)

        retry_delay = retry_delay_generator(
            self._config.initial_retry_delay,
            self._config.retry_delay_multiplier,
            self._config.retry_delay_jitter_factor)

        errors = []

        t0 = -1  # Timer

        while True:
            try:
                self._open_transaction(access_mode=access_mode,
                                       database=self._config.database,
                                       metadata=metadata,
                                       timeout=timeout)
                tx = self._transaction
                try:
                    result = transaction_function(tx, *args, **kwargs)
                except Exception:
                    tx.close()
                    raise
                else:
                    tx.commit()
            except IncompleteCommit:
                raise
            except (ServiceUnavailable, SessionExpired) as error:
                errors.append(error)
                self._disconnect()
            except TransientError as transient_error:
                if not transient_error.is_retriable():
                    raise
                errors.append(transient_error)
            else:
                return result
            if t0 == -1:
                t0 = perf_counter(
                )  # The timer should be started after the first attempt
            t1 = perf_counter()
            if t1 - t0 > self._config.max_transaction_retry_time:
                break
            delay = next(retry_delay)
            log.warning(
                "Transaction failed and will be retried in {}s ({})".format(
                    delay, "; ".join(errors[-1].args)))
            sleep(delay)

        if errors:
            raise errors[-1]
        else:
            raise ServiceUnavailable("Transaction failed")
Ejemplo n.º 17
0
 def acquire(self, access_mode=None):
     for address in resolve(self.address):
         try:
             connection = self.acquire_direct(address)  # should always be a resolved address
         except ServiceUnavailable:
             pass
         else:
             return connection
     raise ServiceUnavailable("Cannot acquire connection to {!r}".format(self.address))
Ejemplo n.º 18
0
def _connect(resolved_address, timeout, keep_alive):
    """

    :param resolved_address:
    :param timeout: seconds
    :param keep_alive: True or False
    :return: socket object
    """

    s = None  # The socket

    try:
        if len(resolved_address) == 2:
            s = socket(AF_INET)
        elif len(resolved_address) == 4:
            s = socket(AF_INET6)
        else:
            raise ValueError(
                "Unsupported address {!r}".format(resolved_address))
        t = s.gettimeout()
        if timeout:
            s.settimeout(timeout)
        log.debug("[#0000]  C: <OPEN> %s", resolved_address)
        s.connect(resolved_address)
        s.settimeout(t)
        keep_alive = 1 if keep_alive else 0
        s.setsockopt(SOL_SOCKET, SO_KEEPALIVE, keep_alive)
    except SocketTimeout:
        log.debug("[#0000]  C: <TIMEOUT> %s", resolved_address)
        log.debug("[#0000]  C: <CLOSE> %s", resolved_address)
        s.close()
        raise ServiceUnavailable(
            "Timed out trying to establish connection to {!r}".format(
                resolved_address))
    except OSError as error:
        log.debug("[#0000]  C: <ERROR> %s %s",
                  type(error).__name__, " ".join(map(repr, error.args)))
        log.debug("[#0000]  C: <CLOSE> %s", resolved_address)
        s.close()
        raise ServiceUnavailable(
            "Failed to establish connection to {!r} (reason {})".format(
                resolved_address, error))
    else:
        return s
Ejemplo n.º 19
0
    def neo4j_driver(cls, *targets, auth=None, routing_context=None, **config):
        """ Create a driver for routing-capable Neo4j service access
        that uses socket I/O and thread-based concurrency.
        """
        from neo4j._exceptions import BoltHandshakeError, BoltSecurityError

        try:
            return Neo4jDriver.open(*targets, auth=auth, routing_context=routing_context, **config)
        except (BoltHandshakeError, BoltSecurityError) as error:
            from neo4j.exceptions import ServiceUnavailable
            raise ServiceUnavailable(str(error)) from error
Ejemplo n.º 20
0
    def bolt_driver(cls, target, *, auth=None, **config):
        """ Create a driver for direct Bolt server access that uses
        socket I/O and thread-based concurrency.
        """
        from neo4j._exceptions import BoltHandshakeError, BoltSecurityError

        try:
            return BoltDriver.open(target, auth=auth, **config)
        except (BoltHandshakeError, BoltSecurityError) as error:
            from neo4j.exceptions import ServiceUnavailable
            raise ServiceUnavailable(str(error)) from error
Ejemplo n.º 21
0
def _connect(resolved_address, timeout=None, **config):
    """

    :param resolved_address:
    :param config:
    :return: socket object
    """
    s = None
    try:
        if len(resolved_address) == 2:
            s = socket(AF_INET)
        elif len(resolved_address) == 4:
            s = socket(AF_INET6)
        else:
            raise ValueError("Unsupported address "
                             "{!r}".format(resolved_address))
        t = s.gettimeout()
        if timeout is None:
            s.settimeout(DEFAULT_CONNECTION_TIMEOUT)
        else:
            s.settimeout(timeout)
        log.debug("[#0000]  C: <OPEN> %s", resolved_address)
        s.connect(resolved_address)
        s.settimeout(t)
        keep_alive = 1 if config.get("keep_alive", DEFAULT_KEEP_ALIVE) else 0
        s.setsockopt(SOL_SOCKET, SO_KEEPALIVE, keep_alive)
    except SocketTimeout:
        log.debug("[#0000]  C: <TIMEOUT> %s", resolved_address)
        log.debug("[#0000]  C: <CLOSE> %s", resolved_address)
        s.close()
        raise ServiceUnavailable("Timed out trying to establish connection "
                                 "to {!r}".format(resolved_address))
    except OSError as error:
        log.debug("[#0000]  C: <ERROR> %s %s",
                  type(error).__name__, " ".join(map(repr, error.args)))
        log.debug("[#0000]  C: <CLOSE> %s", resolved_address)
        s.close()
        raise ServiceUnavailable("Failed to establish connection to {!r} "
                                 "(reason {})".format(resolved_address, error))
    else:
        return s
Ejemplo n.º 22
0
def test_serviceunavailable_raised_from_bolt_protocol_error_with_explicit_style(
):
    error = BoltProtocolError(
        "Driver does not support Bolt protocol version: 0x%06X%02X" % (2, 5),
        address="localhost")

    with pytest.raises(ServiceUnavailable) as e:
        assert error.address == "localhost"
        try:
            raise error
        except BoltProtocolError as error_bolt_protocol:
            error_nested = ServiceUnavailable(str(error_bolt_protocol))
            error_nested.__cause__ = error_bolt_protocol
            raise error_nested

    # The regexp parameter of the match method is matched with the re.search function.
    with pytest.raises(AssertionError):
        e.match("FAIL!")

    e.match("Driver does not support Bolt protocol version: 0x00000205")
    assert e.value.__cause__ is error
Ejemplo n.º 23
0
    def send_all(self):
        """ Send all queued messages to the server.
        """
        if self.closed():
            raise ServiceUnavailable("Failed to write to closed connection {!r} ({!r})".format(
                self.unresolved_address, self.server_info.address))

        if self.defunct():
            raise ServiceUnavailable("Failed to write to defunct connection {!r} ({!r})".format(
                self.unresolved_address, self.server_info.address))

        try:
            self._send_all()
        except (IOError, OSError) as error:
            log.error("Failed to write data to connection "
                      "{!r} ({!r}); ({!r})".
                      format(self.unresolved_address,
                             self.server_info.address,
                             "; ".join(map(repr, error.args))))
            if self.pool:
                self.pool.deactivate(address=self.unresolved_address)
            raise
Ejemplo n.º 24
0
    def _run_transaction(self, access_mode, unit_of_work, *args, **kwargs):

        if not callable(unit_of_work):
            raise TypeError("Unit of work is not callable")

        metadata = getattr(unit_of_work, "metadata", None)
        timeout = getattr(unit_of_work, "timeout", None)

        retry_delay = retry_delay_generator(
            self._config.initial_retry_delay,
            self._config.retry_delay_multiplier,
            self._config.retry_delay_jitter_factor)
        errors = []
        t0 = perf_counter()
        while True:
            try:
                self._open_transaction(access_mode, metadata, timeout)
                tx = self._transaction
                try:
                    result = unit_of_work(tx, *args, **kwargs)
                except Exception:
                    tx.success = False
                    raise
                else:
                    if tx.success is None:
                        tx.success = True
                finally:
                    tx.close()
            except (ServiceUnavailable, SessionExpired,
                    ConnectionExpired) as error:
                errors.append(error)
            except TransientError as error:
                if is_retriable_transient_error(error):
                    errors.append(error)
                else:
                    raise
            else:
                return result
            t1 = perf_counter()
            if t1 - t0 > self._config.max_retry_time:
                break
            delay = next(retry_delay)
            log.warning("Transaction failed and will be retried in {}s "
                        "({})".format(delay, "; ".join(errors[-1].args)))
            sleep(delay)
        if errors:
            raise errors[-1]
        else:
            raise ServiceUnavailable("Transaction failed")
Ejemplo n.º 25
0
    def _verify_routing_connectivity(self):
        from neo4j.exceptions import ServiceUnavailable
        from neo4j._exceptions import BoltHandshakeError

        table = self.get_routing_table()
        routing_info = {}
        for ix in list(table.routers):
            try:
                routing_info[ix] = self._pool.fetch_routing_info(table.routers[0])
            except BoltHandshakeError as error:
                routing_info[ix] = None

        for key, val in routing_info.items():
            if val is not None:
                return routing_info
        raise ServiceUnavailable("Could not connect to any routing servers.")
Ejemplo n.º 26
0
 def _run_transaction(self, access_mode, unit_of_work, *args, **kwargs):
     if not callable(unit_of_work):
         raise TypeError("Unit of work is not callable")
     retry_delay = retry_delay_generator(INITIAL_RETRY_DELAY,
                                         RETRY_DELAY_MULTIPLIER,
                                         RETRY_DELAY_JITTER_FACTOR)
     errors = []
     t0 = perf_counter()
     while True:
         try:
             self._create_transaction()
             self._connect(access_mode)
             self.__begin__()
             tx = self._transaction
             try:
                 result = unit_of_work(tx, *args, **kwargs)
             except:
                 if tx.success is None:
                     tx.success = False
                 raise
             else:
                 if tx.success is None:
                     tx.success = True
             finally:
                 tx.close()
         except (ServiceUnavailable, SessionExpired) as error:
             errors.append(error)
         except TransientError as error:
             if is_retriable_transient_error(error):
                 errors.append(error)
             else:
                 raise
         else:
             return result
         t1 = perf_counter()
         if t1 - t0 > self._max_retry_time:
             break
         sleep(next(retry_delay))
     if errors:
         raise errors[-1]
     else:
         raise ServiceUnavailable("Transaction failed")
Ejemplo n.º 27
0
    def _verify_routing_connectivity(self):
        from neo4j.exceptions import ServiceUnavailable
        from neo4j._exceptions import BoltHandshakeError

        table = self._pool.get_routing_table_for_default_database()
        routing_info = {}
        for ix in list(table.routers):
            try:
                routing_info[ix] = self._pool.fetch_routing_info(
                    address=table.routers[0],
                    timeout=self._default_workspace_config.
                    connection_acquisition_timeout,
                    database=self._default_workspace_config.database)
            except BoltHandshakeError as error:
                routing_info[ix] = None

        for key, val in routing_info.items():
            if val is not None:
                return routing_info
        raise ServiceUnavailable("Could not connect to any routing servers.")
    def fetch_routing_info(self, address, database, bookmarks, timeout):
        """ Fetch raw routing info from a given router address.

        :param address: router address
        :param database: the database name to get routing table for
        :param bookmarks: iterable of bookmark values after which the routing
                          info should be fetched
        :param timeout: connection acquisition timeout in seconds

        :return: list of routing records, or None if no connection
            could be established or if no readers or writers are present
        :raise ServiceUnavailable: if the server does not support
            routing, or if routing support is broken or outdated
        """
        try:
            cx = self._acquire(address, timeout)
            try:
                routing_table = cx.route(
                    database or self.workspace_config.database, bookmarks)
            finally:
                self.release(cx)
        except BoltRoutingError as error:
            # Connection was successful, but routing support is
            # broken. This may indicate that the routing procedure
            # does not exist (for protocol versions prior to 4.3).
            # This error is converted into ServiceUnavailable,
            # therefore surfacing to the application as a signal that
            # routing is broken.
            log.debug("Routing is broken (%s)", error)
            raise ServiceUnavailable(*error.args)
        except ServiceUnavailable as error:
            # The routing table request suffered a connection
            # failure. This should return a null routing table,
            # signalling to the caller to retry the request
            # elsewhere.
            log.debug("Routing is unavailable (%s)", error)
            routing_table = None
        # If the routing table is empty, deactivate the address.
        if not routing_table:
            self.deactivate(address)
        return routing_table
Ejemplo n.º 29
0
    def fetch_routing_info(self, *, address, timeout, database):
        """ Fetch raw routing info from a given router address.

        :param address: router address
        :param timeout: seconds
        :param database: the data base name to get routing table for
        :param address: the address by which the client initially contacted the server as a hint for inclusion in the returned routing table.

        :return: list of routing records or
                 None if no connection could be established
        :raise ServiceUnavailable: if the server does not support routing or
                                   if routing support is broken
        """
        try:
            with self._acquire(address, timeout) as cx:
                return cx.route(database or self.workspace_config.database)
        except BoltRoutingError as error:
            raise ServiceUnavailable(*error.args)
        except ServiceUnavailable:
            self.deactivate(address=address)
            return None
Ejemplo n.º 30
0
    def commit(self):
        """Mark this transaction as successful and close in order to trigger a COMMIT.

        :raise TransactionError: if the transaction is already closed
        """
        if self._closed:
            raise TransactionError("Transaction closed")
        metadata = {}
        self._consume_results()  # DISCARD pending records then do a commit.
        try:
            self._connection.commit(on_success=metadata.update)
            self._connection.send_all()
            self._connection.fetch_all()
        except BoltIncompleteCommitError:
            self._closed = True
            self._on_closed()
            raise ServiceUnavailable("Connection closed during commit")
        self._bookmark = metadata.get("bookmark")
        self._closed = True
        self._on_closed()

        return self._bookmark