public bool TryHandshake(Connection conn) { Stream stream = conn.stream; using (ArrayBuffer getHeader = bufferPool.Take(GetSize)) { if (!ReadHelper.TryRead(stream, getHeader.array, 0, GetSize)) { return(false); } getHeader.count = GetSize; if (!IsGet(getHeader.array)) { Log.Warn($"First bytes from client was not 'GET' for handshake, instead was {Log.BufferToString(getHeader.array, 0, GetSize)}"); return(false); } } string msg = ReadToEndForHandshake(stream); if (string.IsNullOrEmpty(msg)) { return(false); } try { AcceptHandshake(stream, msg); return(true); } catch (ArgumentException e) { Log.InfoException(e); return(false); } }
public bool TryHandshake(Connection conn, Uri uri) { try { Stream stream = conn.stream; byte[] keyBuffer = new byte[16]; using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider()) { rng.GetBytes(keyBuffer); } string key = Convert.ToBase64String(keyBuffer); string keySum = key + Constants.HandshakeGUID; byte[] keySumBytes = Encoding.ASCII.GetBytes(keySum); Log.Verbose($"Handshake Hashing {Encoding.ASCII.GetString(keySumBytes)}"); byte[] keySumHash = SHA1.Create().ComputeHash(keySumBytes); string expectedResponse = Convert.ToBase64String(keySumHash); string handshake = $"GET /chat HTTP/1.1\r\n" + $"Host: {uri.Host}:{uri.Port}\r\n" + $"Upgrade: websocket\r\n" + $"Connection: Upgrade\r\n" + $"Sec-WebSocket-Key: {key}\r\n" + $"Sec-WebSocket-Version: 13\r\n" + "\r\n"; byte[] encoded = Encoding.ASCII.GetBytes(handshake); stream.Write(encoded, 0, encoded.Length); byte[] responseBuffer = new byte[1000]; int?lengthOrNull = ReadHelper.SafeReadTillMatch(stream, responseBuffer, 0, responseBuffer.Length, Constants.endOfHandshake); if (!lengthOrNull.HasValue) { Log.Error("Connected closed before handshake"); return(false); } string responseString = Encoding.ASCII.GetString(responseBuffer, 0, lengthOrNull.Value); string acceptHeader = "Sec-WebSocket-Accept: "; int startIndex = responseString.IndexOf(acceptHeader) + acceptHeader.Length; int endIndex = responseString.IndexOf("\r\n", startIndex); string responseKey = responseString.Substring(startIndex, endIndex - startIndex); if (responseKey != expectedResponse) { Log.Error($"Response key incorrect, Response:{responseKey} Expected:{expectedResponse}"); return(false); } return(true); } catch (Exception e) { Log.Exception(e); return(false); } }
static bool ReadOneMessage(Config config, byte[] buffer) { (Connection conn, int maxMessageSize, bool expectMask, ConcurrentQueue <Message> queue, BufferPool bufferPool) = config; Stream stream = conn.stream; int offset = 0; // read 2 offset = ReadHelper.Read(stream, buffer, offset, Constants.HeaderMinSize); // log after first blocking call Log.Verbose($"Message From {conn}"); if (MessageProcessor.NeedToReadShortLength(buffer)) { offset = ReadHelper.Read(stream, buffer, offset, Constants.ShortLength); } MessageProcessor.ValidateHeader(buffer, maxMessageSize, expectMask); if (expectMask) { offset = ReadHelper.Read(stream, buffer, offset, Constants.MaskSize); } int opcode = MessageProcessor.GetOpcode(buffer); int payloadLength = MessageProcessor.GetPayloadLength(buffer); Log.Verbose($"Header ln:{payloadLength} op:{opcode} mask:{expectMask}"); offset = ReadHelper.Read(stream, buffer, offset, payloadLength); int msgOffset = offset - payloadLength; Log.DumpBuffer($"Raw Header", buffer, 0, msgOffset); switch (opcode) { case 2: HandleArrayMessage(config, buffer, msgOffset, payloadLength); break; case 8: HandleCloseMessage(config, buffer, msgOffset, payloadLength); break; } return(true); }
string ReadToEndForHandshake(Stream stream) { using (ArrayBuffer readBuffer = bufferPool.Take(maxHttpHeaderSize)) { int?readCountOrFail = ReadHelper.SafeReadTillMatch(stream, readBuffer.array, 0, maxHttpHeaderSize, Constants.endOfHandshake); if (!readCountOrFail.HasValue) { return(null); } int readCount = readCountOrFail.Value; string msg = Encoding.ASCII.GetString(readBuffer.array, 0, readCount); Log.Verbose(msg); return(msg); } }
static Header ReadHeader(Config config, byte[] buffer, bool opCodeContinuation = false) { (Connection conn, int maxMessageSize, bool expectMask, ConcurrentQueue <Message> queue, BufferPool bufferPool) = config; Stream stream = conn.stream; Header header = new Header(); // read 2 header.offset = ReadHelper.Read(stream, buffer, header.offset, Constants.HeaderMinSize); // log after first blocking call Log.Verbose($"Message From {conn}"); if (MessageProcessor.NeedToReadShortLength(buffer)) { header.offset = ReadHelper.Read(stream, buffer, header.offset, Constants.ShortLength); } if (MessageProcessor.NeedToReadLongLength(buffer)) { header.offset = ReadHelper.Read(stream, buffer, header.offset, Constants.LongLength); } Log.DumpBuffer($"Raw Header", buffer, 0, header.offset); MessageProcessor.ValidateHeader(buffer, maxMessageSize, expectMask, opCodeContinuation); if (expectMask) { header.offset = ReadHelper.Read(stream, buffer, header.offset, Constants.MaskSize); } header.opcode = MessageProcessor.GetOpcode(buffer); header.payloadLength = MessageProcessor.GetPayloadLength(buffer); header.finished = MessageProcessor.Finished(buffer); Log.Verbose($"Header ln:{header.payloadLength} op:{header.opcode} mask:{expectMask}"); return(header); }
static void ReadOneMessage(Config config, byte[] buffer) { (Connection conn, int maxMessageSize, bool expectMask, ConcurrentQueue <Message> queue, BufferPool bufferPool) = config; Stream stream = conn.stream; Header header = ReadHeader(config, buffer); int msgOffset = header.offset; header.offset = ReadHelper.Read(stream, buffer, header.offset, header.payloadLength); if (header.finished) { switch (header.opcode) { case 2: HandleArrayMessage(config, buffer, msgOffset, header.payloadLength); break; case 8: HandleCloseMessage(config, buffer, msgOffset, header.payloadLength); break; } } else { // todo cache this to avoid allocations Queue <ArrayBuffer> fragments = new Queue <ArrayBuffer>(); fragments.Enqueue(CopyMessageToBuffer(bufferPool, expectMask, buffer, msgOffset, header.payloadLength)); int totalSize = header.payloadLength; while (!header.finished) { header = ReadHeader(config, buffer, opCodeContinuation: true); msgOffset = header.offset; header.offset = ReadHelper.Read(stream, buffer, header.offset, header.payloadLength); fragments.Enqueue(CopyMessageToBuffer(bufferPool, expectMask, buffer, msgOffset, header.payloadLength)); totalSize += header.payloadLength; MessageProcessor.ThrowIfMsgLengthTooLong(totalSize, maxMessageSize); } ArrayBuffer msg = bufferPool.Take(totalSize); msg.count = 0; while (fragments.Count > 0) { ArrayBuffer part = fragments.Dequeue(); part.CopyTo(msg.array, msg.count); msg.count += part.count; part.Release(); } // dump after mask off Log.DumpBuffer($"Message", msg); queue.Enqueue(new Message(conn.connId, msg)); } }