Return the next frame from the socket.

def parse_frame(buf):
    """Return the next frame from the socket."""
    # read header
    while buf.available < 2:
        yield

    data = buf.read(2)
    first_byte, second_byte = struct.unpack('!BB', data)

    fin = (first_byte >> 7) & 1
    rsv1 = (first_byte >> 6) & 1
    rsv2 = (first_byte >> 5) & 1
    rsv3 = (first_byte >> 4) & 1
    opcode = first_byte & 0xf

    # frame-fin = %x0 ; more frames of this message follow
    #           / %x1 ; final frame of this message
    # frame-rsv1 = %x0 ; 1 bit, MUST be 0 unless negotiated otherwise
    # frame-rsv2 = %x0 ; 1 bit, MUST be 0 unless negotiated otherwise
    # frame-rsv3 = %x0 ; 1 bit, MUST be 0 unless negotiated otherwise
    if rsv1 or rsv2 or rsv3:
        raise WebSocketError('Received frame with non-zero reserved bits')

    if opcode > 0x7 and fin == 0:
        raise WebSocketError('Received fragmented control frame')

    if fin == 0 and opcode == OPCODE_CONTINUATION:
        raise WebSocketError(
            'Received new fragment frame with non-zero opcode')

    has_mask = (second_byte >> 7) & 1
    length = (second_byte) & 0x7f

    # Control frames MUST have a payload length of 125 bytes or less
    if opcode > 0x7 and length > 125:
        raise WebSocketError(
            "Control frame payload cannot be larger than 125 bytes")

    # read payload
    if length == 126:
        while buf.available < 2:
            yield

        length = struct.unpack_from('!H', buf.read(2))[0]
    elif length > 126:
        while buf.available < 4:
            yield

        length = struct.unpack_from('!Q', buf.read(4))[0]

    if has_mask:
        while buf.available < 4:
            yield

        mask = buf.read(4)

    if length:
        while buf.available < length:
            yield

        payload = buf.read(length)
    else:
        payload = b''

    if has_mask:
        mask = [ord(i) for i in mask]
        payload = [chr(ord(b) ^ mask[i % 4]) for i, b in enumerate(payload)]

    yield fin, opcode, payload