async def read(
        cls,
        reader: Callable[[int], Awaitable[bytes]],
        *,
        mask: bool,
        max_size: Optional[int] = None,
        extensions: Optional[Sequence["websockets.extensions.base.Extension"]] = None,
    ) -> "Frame":
        """
        Read a WebSocket frame and return a :class:`Frame` object.

        ``reader`` is a coroutine taking an integer argument and reading
        exactly this number of bytes, unless the end of file is reached.

        ``mask`` is a :class:`bool` telling whether the frame should be masked
        i.e. whether the read happens on the server side.

        If ``max_size`` is set and the payload exceeds this size in bytes,
        :exc:`~websockets.exceptions.PayloadTooBig` is raised.

        If ``extensions`` is provided, it's a list of classes with an
        ``decode()`` method that transform the frame and return a new frame.
        They are applied in reverse order.

        This function validates the frame before returning it and raises
        :exc:`~websockets.exceptions.WebSocketProtocolError` if it contains
        incorrect values.

        """
        # Read the header.
        data = await reader(2)
        head1, head2 = struct.unpack("!BB", data)

        # While not Pythonic, this is marginally faster than calling bool().
        fin = True if head1 & 0b10000000 else False
        rsv1 = True if head1 & 0b01000000 else False
        rsv2 = True if head1 & 0b00100000 else False
        rsv3 = True if head1 & 0b00010000 else False
        opcode = head1 & 0b00001111

        if (True if head2 & 0b10000000 else False) != mask:
            raise WebSocketProtocolError("Incorrect masking")

        length = head2 & 0b01111111
        if length == 126:
            data = await reader(2)
            length, = struct.unpack("!H", data)
        elif length == 127:
            data = await reader(8)
            length, = struct.unpack("!Q", data)
        if max_size is not None and length > max_size:
            raise PayloadTooBig(
                f"Payload length exceeds size limit ({length} > {max_size} bytes)"
            )
        if mask:
            mask_bits = await reader(4)

        # Read the data.
        data = await reader(length)
        if mask:
            data = apply_mask(data, mask_bits)

        frame = cls(fin, opcode, data, rsv1, rsv2, rsv3)

        if extensions is None:
            extensions = []
        for extension in reversed(extensions):
            frame = extension.decode(frame, max_size=max_size)

        frame.check()

        return frame
Read a WebSocket frame and return a :class:`Frame` object.

        ``reader`` is a coroutine taking an integer argument and reading
        exactly this number of bytes, unless the end of file is reached.

        ``mask`` is a :class:`bool` telling whether the frame should be masked
        i.e. whether the read happens on the server side.

        If ``max_size`` is set and the payload exceeds this size in bytes,
        :exc:`~websockets.exceptions.PayloadTooBig` is raised.

        If ``extensions`` is provided, it's a list of classes with an
        ``decode()`` method that transform the frame and return a new frame.
        They are applied in reverse order.

        This function validates the frame before returning it and raises
        :exc:`~websockets.exceptions.WebSocketProtocolError` if it contains
        incorrect values.