◐ Shell
clean mode source ↗

Add websocket support by gre-42 · Pull Request #236 · etr/libhttpserver

static size_t ws_receive_frame(unsigned char *frame, size_t *length, int *type)
{
    unsigned char masks[4];
    unsigned char mask;
    unsigned char flength;
    unsigned char idx_first_mask;
    unsigned char idx_first_data;
    size_t data_length, consumed;
    int i;
    int j;

    *type = frame[0] & 0x0F;
#ifdef DEBUG
    printf("[C] ws_receive_frame type %d, processing %zu bytes.\n", *type, *length);
#endif

    if (frame[0] == (WS_FIN | WS_OPCODE_CON_CLOSE_FRAME))
        return 0;

    consumed = 0; // assume no data is consumed
    if (frame[0] == (WS_FIN | WS_OPCODE_TEXT_FRAME) || frame[0] == (WS_FIN | WS_OPCODE_BINARY_FRAME) || frame[0] == (WS_FIN | WS_OPCODE_PING_FRAME) || frame[0] == (WS_FIN | WS_OPCODE_PONG_FRAME))
    {
        idx_first_mask = 2;
        mask = frame[1];
        flength = mask & 0x7F;

        if (flength == 126)
        {
            idx_first_mask = 4;

            // the following 2 bytes interpreted as a 16-bit unsigned integer are the data length
            data_length = ((size_t)frame[2] << 8) | (size_t)frame[3];
        }
        else if (flength == 127)
        {
            idx_first_mask = 10;

            // the following 8 bytes interpreted as a 64-bit unsigned integer
            // (the most significant bit MUST be 0) are the data length
            data_length = ((size_t)frame[2] << 56) | ((size_t)frame[3] << 48) | ((size_t)frame[4] << 40) | ((size_t)frame[5] << 32) | ((size_t)frame[6] << 24) | ((size_t)frame[7] << 16) | ((size_t)frame[8] << 8) | (size_t)frame[9];
        }
        else
        {
            data_length = (size_t)flength;
        }

#ifdef DEBUG
        printf("[C] ws_receive_frame: flength: %d, data_length: %zu\n", flength, data_length);
#endif

        idx_first_data = (unsigned char)(idx_first_mask + 4);

        masks[0] = frame[idx_first_mask + 0];
        masks[1] = frame[idx_first_mask + 1];
        masks[2] = frame[idx_first_mask + 2];
        masks[3] = frame[idx_first_mask + 3];

        // return if there is insufficient data to complete the frame
        if (idx_first_data + data_length > *length)
        {
            // blank out the type
            *type = 0;
            return 0;
        }

        // decode the message
        for (i = idx_first_data, j = 0; j < (int)data_length; i++, j++)
            frame[j] = frame[i] ^ masks[j % 4]; // neat, overwrite the incoming frame buffer

        // the entire WebSocket frame has been processed
        consumed = i; // the number of bytes consumed (equal to idx_first_data + data_length)

        *length = data_length;
        if (*type == WS_OPCODE_TEXT_FRAME)
            frame[j] = '\0';
    }
    else
        printf("[C] ws_receive_frame: received an unknown frame %02X (%d).\n", frame[0], *type);

    return consumed;
}

size_t preamble_ws_frame(char **frame_data, size_t length, unsigned char type)
{
    unsigned char *frame;
    unsigned char idx_first_data;

    if (length <= 125)
    {
        idx_first_data = 2;
    }
    else if (0xFFFF < length)
    {
        idx_first_data = 10;
    }
    else
    {
        idx_first_data = 4;
    }

    frame = malloc(idx_first_data + length);

    if (frame == NULL)
        return 0;

    frame[0] = type;

    if (length <= 125)
    {
        frame[1] = length & 0x7F;
        idx_first_data = 2;
    }
    else if (0xFFFF < length)
    {
        frame[1] = 127;
        frame[2] = (unsigned char)((length >> 56) & 0xFF);
        frame[3] = (unsigned char)((length >> 48) & 0xFF);
        frame[4] = (unsigned char)((length >> 40) & 0xFF);
        frame[5] = (unsigned char)((length >> 32) & 0xFF);
        frame[6] = (unsigned char)((length >> 24) & 0xFF);
        frame[7] = (unsigned char)((length >> 16) & 0xFF);
        frame[8] = (unsigned char)((length >> 8) & 0xFF);
        frame[9] = (unsigned char)(length & 0xFF);
        idx_first_data = 10;
    }
    else
    {
        frame[1] = 126;
        frame[2] = (length >> 8) & 0xFF;
        frame[3] = length & 0xFF;
        idx_first_data = 4;
    }

    *frame_data = (char *)frame;
    return (size_t)idx_first_data;
}