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
Beispiel #2
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
Beispiel #3
0
def test_bolt_protocol_error():
    with pytest.raises(BoltProtocolError) as e:
        error = BoltProtocolError(
            "Driver does not support Bolt protocol version: 0x%06X%02X" %
            (2, 5),
            address="localhost")
        assert error.address == "localhost"
        raise error

    # 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")
Beispiel #4
0
def test_serviceunavailable_raised_from_bolt_protocol_error_with_implicit_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:
            raise ServiceUnavailable(
                str(error_bolt_protocol)) from error_bolt_protocol

    # 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
Beispiel #5
0
def _secure(s, host, ssl_context):
    local_port = s.getsockname()[1]
    # Secure the connection if an SSL context has been provided
    if ssl_context:
        log.debug("[#%04X]  C: <SECURE> %s", local_port, host)
        try:
            sni_host = host if HAS_SNI and host else None
            s = ssl_context.wrap_socket(s, server_hostname=sni_host)
        except (SSLError, OSError) as cause:
            s.close()
            error = BoltSecurityError(message="Failed to establish encrypted connection.", address=(host, local_port))
            error.__cause__ = cause
            raise error
        else:
            # Check that the server provides a certificate
            der_encoded_server_certificate = s.getpeercert(binary_form=True)
            if der_encoded_server_certificate is None:
                s.close()
                raise BoltProtocolError("When using an encrypted socket, the server should always provide a certificate", address=(host, local_port))
    return s
Beispiel #6
0
 def fail(metadata):
     raise BoltProtocolError("RESET failed %r" % metadata, self.unresolved_address)