コード例 #1
0
 def test_reading_partial_line_non_blocking(self):
     mock_socket = mock.Mock()
     mock_socket.recv = mock.MagicMock(side_effect=["partial-".encode(),
                                                    "line\n".encode()])
     protocol = Protocol(mock_socket)
     line = protocol.readline(0.1)
     assert line == 'partial-line'
コード例 #2
0
    def test_disconnect_is_detected_blocking(self):
        mock_socket = mock.Mock()
        mock_socket.recv = mock.MagicMock(side_effect=["partial-".encode(),
                                                       ''.encode()])
        protocol = Protocol(mock_socket)

        with pytest.raises(ClientDisconnected):
            protocol.readline()
コード例 #3
0
    def handle(self):
        self._context.requests_count.incr()
        protocol = Protocol(self.request)
        try:
            line = protocol.readline()
        except ClientDisconnected:
            logger.info("Client disconnected before getting line.")
            protocol.close()
            return

        action = Action.from_line(line)
        if action.is_valid():
            logger.debug("Received valid action: '%s'", action)
        else:
            return self._invalid_request(protocol, line)

        if action.name == const.ACTION_SERVER_SHUTDOWN:
            return handlers.ShutdownActionHandler(
                protocol, action, server=self.server).handle_action()

        if action.name == const.ACTION_PING:
            return handlers.PingActionHandler(protocol, action).handle_action()

        if action.name == const.ACTION_STATS:
            return handlers.StatsActionHandler(
                protocol, action, context=self._context).handle_action()

        if action.name != const.ACTION_LOCK:
            return handlers.InvalidActionActionHandler(protocol,
                                                       action).handle_action()

        lock_name = action.params.get('name')
        if not const.VALID_LOCK_NAME_RE.match(lock_name):
            return handlers.InvalidLockActionHandler(
                protocol, action, lock_name=lock_name).handle_action()

        # Get the Lock, only while holding the GLOBAL LOCK
        # This is done to avoid 2 concurrent clients creating 2 instances of the same lock at the same time
        with self._context.global_lock:
            lock = self._context.locks[lock_name]

        # Acquire lock and proceed, or return failure.
        # If multiple concurrent clients try to get the lock, only one will proceed
        if lock.acquire_non_blocking():
            self._context.lock_acquired_count.incr()
            try:
                return handlers.LockGrantedActionHandler(
                    protocol, action, lock=lock,
                    lock_name=lock_name).handle_action()
            finally:
                lock.release()
        else:
            self._context.lock_not_acquired_count.incr()
            return handlers.LockNotGrantedActionHandler(
                protocol, action, lock_name=lock_name).handle_action()
コード例 #4
0
    def test_reading_after_receiving_multiple_lines_non_blocking(self):
        mock_socket = mock.Mock()
        mock_socket.recv = mock.MagicMock(side_effect=["line1\nline2\nline3\n".encode()])
        protocol = Protocol(mock_socket)

        line = protocol.readline(0.1)
        assert line == 'line1'

        line = protocol.readline(0.1)
        assert line == 'line2'

        line = protocol.readline(0.1)
        assert line == 'line3'
コード例 #5
0
    def __init__(self, host='localhost', port=DEFAULT_PORT, client_id=None):
        """
        Creates a client to connect to the server.

        :param host: hostname of the server
        :param port: port to connect
        """
        self._host = host
        self._port = port
        self._acquired = None
        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self._protocol = Protocol(self._socket)
        self._client_id = client_id
        Utils.validate_client_id(client_id)
コード例 #6
0
class LockClient:
    DEFAULT_PORT = server.TCPServer.DEFAULT_PORT

    def __init__(self, host='localhost', port=DEFAULT_PORT, client_id=None):
        """
        Creates a client to connect to the server.

        :param host: hostname of the server
        :param port: port to connect
        """
        self._host = host
        self._port = port
        self._acquired = None
        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self._protocol = Protocol(self._socket)
        self._client_id = client_id
        Utils.validate_client_id(client_id)

    def connect(self):
        """
        Connects to server.

        :raises ConnectionRefusedError if connection is refused
        :return:
        """
        logger.info("Connecting to '%s:%s'...", self._host, self._port)
        self._socket.connect((self._host, self._port))

    def lock(self, name: str) -> bool:
        """
        Tries to acquire a lock

        :param name: lock name
        :return: boolean indicating if lock as acquired or not
        """
        if not Utils.valid_lock_name(name):
            raise common.InvalidLockNameError(
                "Lock name is invalid: '{lock_name}'".format(lock_name=name))

        response_code = AcquireLockClientAction(self._protocol, None, [
            constants.RESPONSE_OK, constants.RESPONSE_LOCK_NOT_GRANTED,
            constants.RESPONSE_ERR
        ]).handle(lock_name=name, client_id=self._client_id)

        # FIXME: raise specific exception if RESPONSE_ERR is received (ex: InvalidClientId)
        self._acquired = bool(response_code == constants.RESPONSE_OK)
        return self._acquired

    def server_shutdown(self):
        """Send order to shutdown the server"""
        return ClientAction(self._protocol, constants.ACTION_SERVER_SHUTDOWN,
                            [constants.RESPONSE_SHUTTING_DOWN]).handle()

    def ping(self):
        """Send ping to the server"""
        return ClientAction(self._protocol, constants.ACTION_PING,
                            [constants.RESPONSE_PONG]).handle()

    def stats(self):
        """Get stats from server"""
        return GetStatsClientAction(self._protocol, constants.ACTION_STATS,
                                    constants.RESPONSE_STATS_COMING).handle()

    def keepalive(self):
        """Send a keepalive to the server"""
        return ClientAction(self._protocol, constants.ACTION_KEEPALIVE,
                            [constants.RESPONSE_STILL_ALIVE]).handle()

    def release(self):
        """Release the held lock"""
        return ClientAction(self._protocol, constants.ACTION_RELEASE,
                            [constants.RESPONSE_RELEASED]).handle()

    def check_connection(self):
        """
        Raises ClientDisconnected if connection is closed (this is used to detect cases like if the server died.
        """
        self._protocol.check_connection()

    def close(self):
        """Close the socket. As a result of the disconnection, the lock will be released at the server."""
        logger.debug("Closing the socket...")
        self._protocol.close()

    @property
    def acquired(self) -> bool:
        """
        Returns boolean indicating if lock was acquired or not
        :return: True if lock was acquired, False otherwise
        """
        assert self._acquired in (True, False)  # Fail if lock() wasn't called
        return self._acquired
コード例 #7
0
 def _invalid_request(self, protocol: Protocol, line: str):
     logger.warning("Received invalid request: '%s'", line)
     protocol.send(const.RESPONSE_INVALID_REQUEST)
     self.request.close()
コード例 #8
0
 def test_reading_full_line_blocking(self):
     mock_socket = mock.Mock()
     mock_socket.recv = mock.MagicMock(side_effect=["full-line\n".encode()])
     protocol = Protocol(mock_socket)
     line = protocol.readline()
     assert line == 'full-line'
コード例 #9
0
 def test_non_blocking_protocol(self):
     mock_socket = mock.Mock()
     mock_socket.recv = mock.MagicMock(side_effect=[socket.timeout()])
     protocol = Protocol(mock_socket)
     line = protocol.readline(0.1)
     assert line is None