Overview
Features
Download
Documentation
Community
Add-Ons & Services

Receiving Websocket Ping throws incomplete frame exception

Please post support and help requests here.

Receiving Websocket Ping throws incomplete frame exception

Postby satyasidhu » 03 Jul 2014, 16:38

Hi,

I have a server which allows websocket connections and sends a websocket ping message to its clients every 5 seconds.

Using Poco I have written a test client. Every time this test client receives a "ping" message from the server it throws the following exception:
Exception Type: WebSocket Exception
Exception Message: Incomplete frame received


I have other clients written in C#, Python and Javascript. They seem to be working well without any problems.
Even in the unit tests I have not seen tests for ping.

Currently I am using poco 1.4.6p2.
Is this a bug or is there a way around it?

If its a bug, has it been fixed in a later version.

Thanking you.
Best Regards,
Satya Sidhu
satyasidhu
 
Posts: 2
Joined: 03 Jul 2014, 15:28

Re: Receiving Websocket Ping throws incomplete frame excepti

Postby dedvalson » 07 Jul 2014, 19:35

I am using websockets and I do not see this problem in 1.5.2.

Don
dedvalson
 
Posts: 9
Joined: 27 Feb 2014, 19:57

Re: Receiving Websocket Ping throws incomplete frame excepti

Postby satyasidhu » 09 Jul 2014, 09:34

When the server sends a PING it only sends a 2 byte long frame. Basically containing the Opcode and the length.
Here the Opcode is 9 and the length is 0.

The default implementation expects more than a two byte frame and thus on receiving this two byte frame throws and incomplete frame exception.I think a two byte frame with length 0 is valid (As it is accepted by JavaScript, Python and C#. Also it makes sense as all the information needed is provided in the first two bytes) and for that I have added the following code:

Code: Select all
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;
}



Here the code that I have added is
Code: Select all
/////////////////////////////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;
   }
//////////////////////////////////////////////////////////////////////////////////////


The return value is the number of bytes returned, but since it is a control frame I am returning a non-zero value of -1 as it does not lead to any changes in my client. But returning a zero would be good as well.

I think if there is no payload then a two byte frame with a length of zero is a perfectly valid frame.
satyasidhu
 
Posts: 2
Joined: 03 Jul 2014, 15:28

Re: Receiving Websocket Ping throws incomplete frame excepti

Postby dedvalson » 11 Jul 2014, 16:43

The following code is working properly for me.

Code: Select all
           
                n = Ws->receiveFrame(buffer, sizeof(buffer), flags);
                if ((flags & WebSocket::FRAME_OP_BITMASK) == WebSocket::FRAME_OP_CLOSE)
                {
                    poco_notice_f1(L, "WebSocket received shutdown from peer, token %s", Td?Td->token():"");
                    if (!ShutdownSent) {
                        Ws->shutdown();
                        ShutdownSent = true;
                    }
                    break;
                }
                if ((flags & WebSocket::FRAME_OP_BITMASK) == WebSocket::FRAME_OP_PING) {
                    poco_information_f1(L, "WebSocket received ping from peer, token %s", Td?Td->token():"");
                    Mutex::ScopedLock l(TransmitMutex);
                    Ws->sendFrame (buffer, n, WebSocket::FRAME_OP_PONG);
                    continue;
                }
                if ((flags & WebSocket::FRAME_OP_BITMASK) == WebSocket::FRAME_OP_PONG) {
                    poco_information_f1(L, "WebSocket received pong from peer, token %s", Td?Td->token():"");
                    continue;
                }

dedvalson
 
Posts: 9
Joined: 27 Feb 2014, 19:57


Return to Support

Who is online

Users browsing this forum: No registered users and 4 guests