/// <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>
            /// 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();
                }
            }
Example #4
0
        /// <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);
        }