Skip to content

2 byte frame with payload length of 0 throws "Incomplete Frame Received" exception #490

@satyasidhu

Description

@satyasidhu

Appweb server sends a PING frame to a C++/Poco client which throws the following exception:
Exception Type: WebSocket Exception
Exception Message: Incomplete frame received

A two byte frame with palyload length of zero is valid as it has all the information needed, but the current Poco implementation requires a frame larger than 2 bytes else it throws an "Invalid frame exception".
In the following I have added the code within
///////////////////////////////////SATYA SIDHU: BUG FIXED//////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
that ensures that a two byte frame with a payload length of zero is valid as is with other libraries such as C#, JavaScript and Python etc. I am returning -1 because it is a control frame and also to prevent changes in my client code, but a return value of zero would also be fine.

int WebSocketImpl::receiveBytes(void* buffer, int length, int)
{
    char header[MAX_HEADER_LENGTH];
    int n = receiveNBytes(header, 2);
    if (n <= 0)
    {
        _frameFlags = 0;
        return n;
    }
    poco_assert (n == 2);
    Poco::UInt8 lengthByte = static_cast<Poco::UInt8>(header[1]);
    int maskOffset = 0;
    if (lengthByte & FRAME_FLAG_MASK) maskOffset += 4;
    lengthByte &= 0x7f;

    if (lengthByte + 2 + maskOffset < MAX_HEADER_LENGTH)
    {
        n = receiveNBytes(header + 2, lengthByte + maskOffset);
    }
    else
    {
        n = receiveNBytes(header + 2, MAX_HEADER_LENGTH - 2);
    }

    /////////////////////////////SATYA SIDHU: BUG FIXED////////////////////////////////////
    if ((0 == n) && (0 == lengthByte))
    {
        Poco::MemoryInputStream istr(header, 2);    // 2 bytes have been recieved so parse them only.
        Poco::BinaryReader reader(istr, Poco::BinaryReader::NETWORK_BYTE_ORDER);
        Poco::UInt8 flags;
        char mask[4];
        reader >> flags >> lengthByte;
        _frameFlags = flags;
        return -1;
    }
    //////////////////////////////////////////////////////////////////////////////////////

    if (n <= 0) throw WebSocketException("Incomplete frame received", WebSocket::WS_ERR_INCOMPLETE_FRAME);

    n += 2;
    Poco::MemoryInputStream istr(header, n);
    Poco::BinaryReader reader(istr, Poco::BinaryReader::NETWORK_BYTE_ORDER);
    Poco::UInt8 flags;
    char mask[4];
    reader >> flags >> lengthByte;
    _frameFlags = flags;
    int payloadLength = 0;
    int payloadOffset = 2;
    if ((lengthByte & 0x7f) == 127)
    {
        Poco::UInt64 l;
        reader >> l;
        if (l > length) throw WebSocketException(Poco::format("Insufficient buffer for payload size %Lu", l), WebSocket::WS_ERR_PAYLOAD_TOO_BIG);
        payloadLength = static_cast<int>(l);
        payloadOffset += 8;
    }
    else if ((lengthByte & 0x7f) == 126)
    {
        Poco::UInt16 l;
        reader >> l;
        if (l > length) throw WebSocketException(Poco::format("Insufficient buffer for payload size %hu", l), WebSocket::WS_ERR_PAYLOAD_TOO_BIG);
        payloadLength = static_cast<int>(l);
        payloadOffset += 2;
    }
    else
    {
        Poco::UInt8 l = lengthByte & 0x7f;
        if (l > length) throw WebSocketException(Poco::format("Insufficient buffer for payload size %u", unsigned(l)), WebSocket::WS_ERR_PAYLOAD_TOO_BIG);
        payloadLength = static_cast<int>(l);
    }
    if (lengthByte & FRAME_FLAG_MASK)
    {
        reader.readRaw(mask, 4);
        payloadOffset += 4;
    }
    int received = 0;
    if (payloadOffset < n)
    {
        std::memcpy(buffer, header + payloadOffset, n - payloadOffset);
        received = n - payloadOffset;
    }
    if (received < payloadLength)
    {
        n = receiveNBytes(reinterpret_cast<char*>(buffer) + received, payloadLength - received);
        if (n <= 0) throw WebSocketException("Incomplete frame received", WebSocket::WS_ERR_INCOMPLETE_FRAME);
        received += n;
    }
    if (lengthByte & FRAME_FLAG_MASK)
    {
        char* p = reinterpret_cast<char*>(buffer);
        for (int i = 0; i < received; i++)
        {
            p[i] ^= mask[i % 4];
        }
    }
    return received;
}

Websocket frame information obtained from the following link:
http://chimera.labs.oreilly.com/books/1230000000545/ch17.html#_websocket_protocol

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions