/// <summary> /// Updates the local continuation. /// </summary> /// <param name="frame"></param> private void UpdateContinuation(ref WebSocketFrame continuationFrame, WebSocketFrame frame) { if (continuationFrame == null) { continuationFrame = frame; // set initial frame } else { byte[] frameData = new byte[continuationFrame.Data.Length + frame.Data.Length]; Buffer.BlockCopy(continuationFrame.Data, 0, frameData, 0, continuationFrame.Data.Length); Buffer.BlockCopy(frame.Data, 0, frameData, continuationFrame.Data.Length, frame.Data.Length); switch (continuationFrame.Operation) { case WebSocketFrame.WebSocketFrameOperation.BinaryFrame: continuationFrame = WebSocketFrame.CreateBinaryFrame(frameData, false, false); break; case WebSocketFrame.WebSocketFrameOperation.TextFrame: continuationFrame = WebSocketFrame.CreateTextFrame(frameData, false, false); break; } } }
/// <summary> /// Handles incoming raw frames and translates them into WebSocketFrame(s) /// </summary> /// <param name="channel"></param> /// <param name="request"></param> private void HandleIncomingFrames(ISockNetChannel channel, ref object data) { if (!(data is ChunkedBuffer)) { return; } ChunkedBuffer stream = (ChunkedBuffer)data; long startingPosition = stream.ReadPosition; try { WebSocketFrame frame = WebSocketFrame.ParseFrame(stream.Stream); if (combineContinuations) { if (frame.IsFinished) { UpdateContinuation(ref continuationFrame, frame); data = continuationFrame; continuationFrame = null; } else { UpdateContinuation(ref continuationFrame, frame); } } else { data = frame; } if (SockNetLogger.DebugEnabled) { SockNetLogger.Log(SockNetLogger.LogLevel.DEBUG, this, "Received WebSocket message. Size: {0}, Type: {1}, IsFinished: {2}", frame.Data.Length, Enum.GetName(typeof(WebSocketFrame.WebSocketFrameOperation), frame.Operation), frame.IsFinished); } } catch (EndOfStreamException) { // websocket frame isn't done stream.ReadPosition = startingPosition; } catch (ArgumentOutOfRangeException) { // websocket frame isn't done stream.ReadPosition = startingPosition; } catch (Exception e) { SockNetLogger.Log(SockNetLogger.LogLevel.ERROR, this, "Unable to parse web-socket request", e); channel.Close(); } }
/// <summary> /// Parses a frame from the given stream. /// </summary> /// <param name="stream"></param> /// <returns></returns> public static WebSocketFrame ParseFrame(Stream stream) { WebSocketFrame frame = new WebSocketFrame(); BinaryReader binaryReader = new BinaryReader(stream, (Encoding)WebSocketFrame.UTF8); byte finRsvAndOp = binaryReader.ReadByte(); if (!Enum.IsDefined(typeof(WebSocketFrame.WebSocketFrameOperation), (byte)((int)finRsvAndOp & 15))) { throw new ArgumentException("Invalid operation: " + ((int)finRsvAndOp & 15)); } frame.IsFinished = ((int)finRsvAndOp & 128) != 0; frame.Reserved1 = ((int)finRsvAndOp & 64) != 0; frame.Reserved2 = ((int)finRsvAndOp & 32) != 0; frame.Reserved3 = ((int)finRsvAndOp & 16) != 0; frame.Operation = (WebSocketFrame.WebSocketFrameOperation)(byte)((int)finRsvAndOp & 15); byte maskAndLength = binaryReader.ReadByte(); bool isMasked = (maskAndLength & 128) != 0; int length = maskAndLength & 127; switch (length) { case 126: { length = (int)(ushort)IPAddress.HostToNetworkOrder((short)binaryReader.ReadUInt16()); break; } case 127: { length = Convert.ToInt32((ulong)IPAddress.HostToNetworkOrder((long)binaryReader.ReadUInt64())); break; } } if (isMasked) { frame.Mask = binaryReader.ReadBytes(4); } frame.Data = binaryReader.ReadBytes(length); if (frame.Data.Length != length) { throw new EndOfStreamException(); } if (isMasked) { for (int i = 0; i < frame.Data.Length; i++) { frame.Data[i] = (byte)(frame.Data[i] ^ frame.Mask[i % 4]); } } return frame; }
public void TestIncompleteBufferParsing() { ObjectPool <byte[]> pool = new ObjectPool <byte[]>(() => { return(new byte[1024]); }); WebSocketEchoServer server = new WebSocketEchoServer(pool); try { server.Start(); BlockingCollection <object> blockingCollection = new BlockingCollection <object>(); ClientSockNetChannel client = (ClientSockNetChannel)SockNetClient.Create(server.Endpoint, ClientSockNetChannel.DefaultNoDelay, ClientSockNetChannel.DefaultTtl, pool) .AddModule(new WebSocketClientSockNetChannelModule("/", "localhost", (ISockNetChannel sockNetClient) => { blockingCollection.Add(true); })); client.Connect().WaitForValue(TimeSpan.FromSeconds(5)); object currentObject; Assert.IsTrue(blockingCollection.TryTake(out currentObject, DEFAULT_ASYNC_TIMEOUT)); Assert.IsTrue((bool)currentObject); client.Pipe.AddIncomingLast <WebSocketFrame>((ISockNetChannel sockNetClient, ref WebSocketFrame data) => { blockingCollection.Add(data); }); string body1 = new string('A', 4913) + "X"; WebSocketFrame frame1 = WebSocketFrame.CreateTextFrame( "STS/1.0 200 OK" + "\r\n" + "s:7R" + "\r\n" + "n:bytes 0-4915/5000" + "\r\n" + "l:4916" + "\r\n" + "" + "\r\n" + body1 + "\r\n", false); client.Send(frame1); string body2 = new string('B', 81) + "Y"; WebSocketFrame frame2 = WebSocketFrame.CreateTextFrame( "STS/1.0 200 OK" + "\r\n" + "s:7R" + "\r\n" + "n:bytes 4916-4999/5000" + "\r\n" + "l:84" + "\r\n" + "" + "\r\n" + body2 + "\r\n", false); client.Send(frame2); Assert.IsTrue(blockingCollection.TryTake(out currentObject, DEFAULT_ASYNC_TIMEOUT)); Assert.IsTrue(currentObject is WebSocketFrame); Assert.AreEqual(frame1.DataAsString, ((WebSocketFrame)currentObject).DataAsString); Console.WriteLine("Got response: \n" + ((WebSocketFrame)currentObject).DataAsString); Assert.IsTrue(blockingCollection.TryTake(out currentObject, DEFAULT_ASYNC_TIMEOUT)); Assert.IsTrue(currentObject is WebSocketFrame); Assert.AreEqual(frame2.DataAsString, ((WebSocketFrame)currentObject).DataAsString); Console.WriteLine("Got response: \n" + ((WebSocketFrame)currentObject).DataAsString); client.Disconnect().WaitForValue(TimeSpan.FromSeconds(5)); } finally { server.Stop(); } }
public void TestSmallMessagesInParallel() { ObjectPool <byte[]> pool = new ObjectPool <byte[]>(() => { return(new byte[1024]); }); Random random = new Random(this.GetHashCode() ^ DateTime.Now.Millisecond); WebSocketEchoServer server = new WebSocketEchoServer(pool); try { server.Start(); BlockingCollection <object> blockingCollection = new BlockingCollection <object>(); ClientSockNetChannel client = (ClientSockNetChannel)SockNetClient.Create(server.Endpoint, ClientSockNetChannel.DefaultNoDelay, ClientSockNetChannel.DefaultTtl, pool) .AddModule(new WebSocketClientSockNetChannelModule("/", "localhost", (ISockNetChannel sockNetClient) => { blockingCollection.Add(true); })); client.Connect().WaitForValue(TimeSpan.FromSeconds(5)); object currentObject; Assert.IsTrue(blockingCollection.TryTake(out currentObject, DEFAULT_ASYNC_TIMEOUT)); Assert.IsTrue((bool)currentObject); client.Pipe.AddIncomingLast <WebSocketFrame>((ISockNetChannel sockNetClient, ref WebSocketFrame data) => { blockingCollection.Add(data); }); int numberOfMessages = 100; byte[][] expectedResults = new byte[numberOfMessages][]; for (int i = 0; i < numberOfMessages; i++) { ThreadPool.QueueUserWorkItem((object state) => { int index = (int)state; byte[] messageData = new byte[random.Next(50, 100)]; random.NextBytes(messageData); messageData[0] = (byte)(index >> 0); messageData[1] = (byte)(index >> 8); messageData[2] = (byte)(index >> 16); messageData[3] = (byte)(index >> 24); expectedResults[index] = messageData; client.Send(WebSocketFrame.CreateBinaryFrame(messageData)); // simulate GC if (index % Math.Max(numberOfMessages / 10, 1) == 0) { GC.Collect(); GC.WaitForPendingFinalizers(); } }, i); } for (int i = 0; i < numberOfMessages; i++) { Assert.IsTrue(blockingCollection.TryTake(out currentObject, DEFAULT_ASYNC_TIMEOUT)); Assert.IsTrue(currentObject is WebSocketFrame); // simulate GC if (i % Math.Max(numberOfMessages / 10, 1) == 0) { GC.Collect(); GC.WaitForPendingFinalizers(); } byte[] incomingData = ((WebSocketFrame)currentObject).Data; int index = 0; index |= incomingData[0] << 0; index |= incomingData[1] << 8; index |= incomingData[2] << 16; index |= incomingData[3] << 24; Console.WriteLine(index); AreArraysEqual(expectedResults[index], incomingData); } client.Disconnect().WaitForValue(TimeSpan.FromSeconds(5)); } finally { server.Stop(); } }
public void Start(bool isTls = false, bool doContinuations = false) { server = SockNetServer.Create(GetLocalIpAddress(), 0, ServerSockNetChannel.DefaultBacklog, pool); try { server.AddModule(new WebSocketServerSockNetChannelModule("/", "localhost")); if (isTls) { byte[] rawCert = CertificateUtil.CreateSelfSignCertificatePfx("CN=\"test\"; C=\"USA\"", DateTime.Today.AddDays(-10), DateTime.Today.AddDays(+10)); server.BindWithTLS(new X509Certificate2(rawCert), (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) => { return(true); }).WaitForValue(TimeSpan.FromSeconds(5)); } else { server.Bind().WaitForValue(TimeSpan.FromSeconds(5)); } Assert.IsTrue(server.IsActive); server.Pipe.AddIncomingLast <WebSocketFrame>((ISockNetChannel channel, ref WebSocketFrame data) => { if (doContinuations) { int perFrameSize = data.Data.Length / 3; for (int i = 0; i < 3; i++) { bool isDone = false; byte[] rawData; if (i + 1 < 3) { rawData = new byte[perFrameSize]; isDone = false; } else { rawData = new byte[data.Data.Length - (perFrameSize * 2)]; isDone = true; } Buffer.BlockCopy(data.Data, i * perFrameSize, rawData, 0, rawData.Length); if (data.Operation == WebSocketFrame.WebSocketFrameOperation.BinaryFrame) { channel.Send(WebSocketFrame.CreateBinaryFrame(rawData, true, i != 0, isDone)); } else { channel.Send(WebSocketFrame.CreateTextFrame(rawData, true, i != 0, isDone)); } } } else { channel.Send(data); } }); } catch (Exception) { Stop(); } }
public void TestLargeMessage() { ObjectPool <byte[]> pool = new ObjectPool <byte[]>(() => { return(new byte[1024]); }); DummySockNetChannel channel = new DummySockNetChannel() { State = null, IsActive = true, BufferPool = pool }; channel.Pipe = new SockNetChannelPipe(channel); WebSocketClientSockNetChannelModule module = new WebSocketClientSockNetChannelModule("/test", "test", null); channel.AddModule(module); channel.Connect(); object sent = null; channel.outgoing.TryTake(out sent, 5000); Assert.IsTrue(sent is HttpRequest); HttpResponse handshakeResponse = null; using (HttpRequest request = (HttpRequest)sent) { handshakeResponse = new HttpResponse(null) { Code = "200", Reason = "OK", Version = "HTTP/1.1" }; handshakeResponse.Header[WebSocketUtil.WebSocketAcceptHeader] = WebSocketUtil.GenerateAccept(request.Header[WebSocketUtil.WebSocketKeyHeader]); } object receiveResponse = handshakeResponse; channel.Receive(ref receiveResponse); Random random = new Random(this.GetHashCode() ^ (int)DateTime.Now.Subtract(new DateTime(2000, 1, 1)).TotalMilliseconds); for (int n = 0; n < 100; n++) { byte[] data = new byte[random.Next(50000, 150000)]; random.NextBytes(data); WebSocketFrame frame = WebSocketFrame.CreateBinaryFrame(data, false); using (ChunkedBuffer buffer = ToBuffer(frame)) { receiveResponse = buffer; channel.Receive(ref receiveResponse); Assert.IsTrue(receiveResponse is WebSocketFrame); Assert.AreEqual(data.Length, ((WebSocketFrame)receiveResponse).Data.Length); for (int i = 0; i < data.Length; i++) { Assert.AreEqual(data[i], ((WebSocketFrame)receiveResponse).Data[i]); } buffer.Close(); } } Console.WriteLine("Pool stats: " + pool.ObjectsInPool + "/" + pool.TotalNumberOfObjects); }
public void TestContinuation() { ObjectPool <byte[]> pool = new ObjectPool <byte[]>(() => { return(new byte[1024]); }); DummySockNetChannel channel = new DummySockNetChannel() { State = null, IsActive = true, BufferPool = pool }; channel.Pipe = new SockNetChannelPipe(channel); WebSocketClientSockNetChannelModule module = new WebSocketClientSockNetChannelModule("/test", "test", null); channel.AddModule(module); channel.Connect(); object sent = null; channel.outgoing.TryTake(out sent, 5000); Assert.IsTrue(sent is HttpRequest); HttpRequest request = (HttpRequest)sent; HttpResponse handshakeResponse = new HttpResponse(channel.BufferPool) { Code = "200", Reason = "OK", Version = "HTTP/1.1" }; handshakeResponse.Header[WebSocketUtil.WebSocketAcceptHeader] = WebSocketUtil.GenerateAccept(request.Header[WebSocketUtil.WebSocketKeyHeader]); object receiveResponse = handshakeResponse; channel.Receive(ref receiveResponse); WebSocketFrame continuation1 = WebSocketFrame.CreateTextFrame("This ", false, false, false); ChunkedBuffer buffer = ToBuffer(continuation1); receiveResponse = buffer; channel.Receive(ref receiveResponse); buffer.Close(); Assert.IsTrue(receiveResponse is ChunkedBuffer); WebSocketFrame continuation2 = WebSocketFrame.CreateTextFrame("is ", false, true, false); buffer = ToBuffer(continuation2); receiveResponse = buffer; channel.Receive(ref receiveResponse); buffer.Close(); Assert.IsTrue(receiveResponse is ChunkedBuffer); WebSocketFrame continuation3 = WebSocketFrame.CreateTextFrame("awesome!", false, true, true); buffer = ToBuffer(continuation3); receiveResponse = buffer; channel.Receive(ref receiveResponse); buffer.Close(); Assert.IsTrue(receiveResponse is WebSocketFrame); Assert.AreEqual("This is awesome!", ((WebSocketFrame)receiveResponse).DataAsString); Console.WriteLine("Pool stats: " + pool.ObjectsInPool + "/" + pool.TotalNumberOfObjects); }