private bool TryParseMessageHeaderFromReceiveBuffer(out MessageHeader resultHeader)
{
Debug.Assert(_receiveBufferCount >= 2, $"Expected to at least have the first two bytes of the header.");
var header = new MessageHeader();
header.Fin = (_receiveBuffer[_receiveBufferOffset] & 0x80) != 0;
bool reservedSet = (_receiveBuffer[_receiveBufferOffset] & 0x70) != 0;
header.Opcode = (MessageOpcode)(_receiveBuffer[_receiveBufferOffset] & 0xF);
bool masked = (_receiveBuffer[_receiveBufferOffset + 1] & 0x80) != 0;
header.PayloadLength = _receiveBuffer[_receiveBufferOffset + 1] & 0x7F;
ConsumeFromBuffer(2);
// Read the remainder of the payload length, if necessary
if (header.PayloadLength == 126)
{
Debug.Assert(_receiveBufferCount >= 2, $"Expected to have two bytes for the payload length.");
header.PayloadLength = (_receiveBuffer[_receiveBufferOffset] << 8) | _receiveBuffer[_receiveBufferOffset + 1];
ConsumeFromBuffer(2);
}
else if (header.PayloadLength == 127)
{
Debug.Assert(_receiveBufferCount >= 8, $"Expected to have eight bytes for the payload length.");
header.PayloadLength = 0;
for (int i = 0; i < 8; i++)
{
header.PayloadLength = (header.PayloadLength << 8) | _receiveBuffer[_receiveBufferOffset + i];
}
ConsumeFromBuffer(8);
}
bool shouldFail = reservedSet;
if (masked)
{
if (!_isServer)
{
shouldFail = true;
}
header.Mask = CombineMaskBytes(_receiveBuffer, _receiveBufferOffset);
// Consume the mask bytes
ConsumeFromBuffer(4);
}
// Do basic validation of the header
switch (header.Opcode)
{
case MessageOpcode.Continuation:
if (_lastReceiveHeader.Fin)
{
// Can't continue from a final message
shouldFail = true;
}
break;
case MessageOpcode.Binary:
case MessageOpcode.Text:
if (!_lastReceiveHeader.Fin)
{
// Must continue from a non-final message
shouldFail = true;
}
break;
case MessageOpcode.Close:
case MessageOpcode.Ping:
case MessageOpcode.Pong:
if (header.PayloadLength > MaxControlPayloadLength || !header.Fin)
{
// Invalid control messgae
shouldFail = true;
}
break;
default:
// Unknown opcode
shouldFail = true;
break;
}
// Return the read header
resultHeader = header;
return !shouldFail;
}