internal static PayloadHeader Decode(List <byte> buffer, int startPosition) { var header = new PayloadHeader(); // Decode the first byte var position = startPosition; if (position >= buffer.Count) { return(null); } header.FinishedBit = (buffer[position] & 128) == 128; header.OpCode = (WebSocketOpCode)(buffer[position] & 15); position++; // Decode the second byte if (position >= buffer.Count) { return(null); } header.Masked = (buffer[position] & 128) == 128; header.PayloadLength = buffer[position] & 127; position++; // Calculate the real length of the payload (but exit if we don't have enough bytes) if (header.PayloadLength == 126) { if (position + 1 >= buffer.Count) { return(null); } header.PayloadLength = (buffer[position++] << 8) | buffer[position++]; } else if (header.PayloadLength == 127) { if (position + 7 >= buffer.Count) { return(null); } header.PayloadLength = (buffer[position++] << 56) | (buffer[position++] << 48) | (buffer[position++] << 40) | (buffer[position++] << 32) | (buffer[position++] << 24) | (buffer[position++] << 16) | (buffer[position++] << 8) | buffer[position++]; } // Get the mask key bytes if (header.Masked) { if (position + 3 >= buffer.Count) { return(null); } header.MaskKey = new byte[4]; buffer.CopyTo(position, header.MaskKey, 0, 4); position += 4; } // Return decoded payload header header.Size = position - startPosition; return(header); }
private Packet DecodeBuffer() { var position = 0; var header = PayloadHeader.Decode(largeBuffer, position); if (header == null) { return(null); } position += header.Size; // Confirm that we have retrieved enough bytes, otherwise we need to wait for more data to come in var totalExpectedSize = header.Size + header.PayloadLength; if (largeBuffer.Count < totalExpectedSize) { return(null); } // Copy the payload into its own array, unmasking it if necessary into a readable form var payloadBytes = new byte[header.PayloadLength]; if (header.Masked) { for (var i = 0; i < header.PayloadLength; i++) { payloadBytes[i] = (byte)(largeBuffer[position + i] ^ header.MaskKey[i % 4]); } } else { largeBuffer.CopyTo(position, payloadBytes, 0, header.PayloadLength); } // Advance the position position += header.PayloadLength; // We only accept these types of opcode switch (header.OpCode) { case WebSocketOpCode.Close: largeBuffer.RemoveRange(0, position); return(new Packet(RemoteEndPoint, WebSocketAction.Disconnect, "Intentional client disconnection.")); case WebSocketOpCode.Text: case WebSocketOpCode.Continuation: case WebSocketOpCode.Ping: // Add to the payload largeBuffer.RemoveRange(0, position); incompletePayload.AddRange(payloadBytes); // Do we have a complete payload? if (!header.FinishedBit) { return(DecodeBuffer()); } // Convert the completed payload into a string var payloadText = Encoding.UTF8.GetString(incompletePayload.ToArray()); incompletePayload.Clear(); // Raise a text packet for processing if (header.OpCode != WebSocketOpCode.Ping) { return(new Packet(RemoteEndPoint, WebSocketAction.SendText, payloadText)); } // Send a pong and get the next packet Send(WebSocketOpCode.Pong, payloadText); return(DecodeBuffer()); case WebSocketOpCode.Pong: // Simply get the next packet largeBuffer.RemoveRange(0, position); return(DecodeBuffer()); default: throw new NotImplementedException("Web socket opcode " + header.OpCode + " is currently not supported."); } }