public void WebSocketFrame_TryParse_Valid_DataFrame_Text_SingleFrame() { string testContent = "test!"; bool fin = true; WebSocketOPCode OPCode = WebSocketOPCode.Text; byte[] payload = System.Text.Encoding.UTF8.GetBytes(testContent); // Manually craft a WebSocketFrame Random rnd = new Random(); byte[] maskingK = new byte[4]; // The masking key is supposed to be cryptographically secure but this will suffice for testing purposes rnd.NextBytes(maskingK); }
/// <summary> /// Tries to parse a byte[] header into a SocketDataFrame object. /// Returns a null reference if object cannot be parsed /// </summary> /// <param name="headerBytes">A byte array containing the read frame header bytes</param> /// <param name="socket">A reference to the socket from which to parse the SocketFrame</param> /// <returns> /// If pasrse is successful, an Object of a type that is derived from SocketFrame.abstract Returns a null pointer otherwise. /// </returns> /// <remarks> /// This method is not exactly like the Int*.TryParse() methods as it doesn't take an 'out' parameter and return a /// boolean value but rather returns either the parsed object reference or a null reference, which means that callers of this method need to check /// for null before using the return value. /// Furthermore, if the parse is successful, a caller should check the type of the object that is returned to, for example, /// differenciate between a SocketDataFrame and a SocketControlFrame, which are both derived from SocketFrame. /// It is necessary to pass a reference to the socket because of the way the WebSocket protocol is made.abstract It is impossible to know /// the length of the frame before having parsed the headers hence it is possible that more bytes will need to be read from the socket buffer. /// </remarks> public static WebSocketFrame TryParse(byte[] headerBytes, Socket socket) { int headerSize = headerBytes.Length; bool fin = (headerBytes[0] >> 7) != 0; WebSocketOPCode opcode = (WebSocketOPCode)((byte)(headerBytes[0] & 0b00001111)); bool masked = (headerBytes[1] & 0b10000000) != 0; ushort contentLength = (ushort)(headerBytes[1] & 0b01111111); if (contentLength <= 126) { if (contentLength == 126) { headerSize = 4; byte[] largerHeader = new byte[headerSize]; headerBytes.CopyTo(largerHeader, 0); // Read next two bytes and interpret them as the content length socket.Receive(largerHeader, 2, 2, SocketFlags.None); contentLength = (ushort)(largerHeader[2] << 8 | largerHeader[3]); } byte[] maskingKey = new byte[4]; socket.Receive(maskingKey); byte[] contentBuffer = new byte[contentLength]; if (contentLength > 0) { socket.Receive(contentBuffer); } WebSocketFrame frame; if (opcode == WebSocketOPCode.Text || opcode == WebSocketOPCode.Binary) { frame = new WebSocketDataFrame(fin, masked, contentBuffer, (WebSocketDataFrame.DataFrameType)opcode, ApplyMask(contentBuffer, maskingKey)); } else if (opcode == WebSocketOPCode.Close) { WebSocketCloseFrame closeFrame = new WebSocketCloseFrame(); if (contentLength >= 2) { byte[] unmasked = ApplyMask(contentBuffer, maskingKey); WebSocketCloseCode closeCode = (WebSocketCloseCode)BitConverter.ToUInt16(unmasked); closeFrame.CloseCode = closeCode; if (contentLength > 2) { byte[] closeReasonBytes = new byte[contentLength - 2]; Array.Copy(contentBuffer, 2, closeReasonBytes, 0, closeReasonBytes.Length); closeFrame.CloseReason = System.Text.Encoding.UTF8.GetString(closeReasonBytes); } } frame = closeFrame; } else { frame = new WebSocketControlFrame(fin, masked, (WebSocketOPCode)opcode); } return(frame); } return(null); }
/// <summary> /// Initializes a new instance of the SocketControlFrame class /// </summary> /// <param name="fin"></param> /// <param name="masked"></param> /// <param name="controlOpCode">The control OPCode of the current SocketControlFrame</param> public WebSocketControlFrame(bool fin, bool masked, WebSocketOPCode controlOpCode) : base(true, false, new byte[0], WebSocketDataFrame.DataFrameType.Binary) { this.opcode = (byte)controlOpCode; }