protected void Push(NetContext context, WebSocketsFrame frame)
 {
     lock (this)
     {
         int maxQuota = context.Handler.MaxIncomingQuota;
         if (maxQuota > 0 && ProtocolProcessor.Sum(holder, f =>
         {
             var typed = f as WebSocketsFrame;
             return(typed == null ? 0 : typed.GetLengthEstimate());
         }) + frame.GetLengthEstimate() > maxQuota)
         {
             throw new InvalidOperationException("Inbound quota exceeded");
         }
         ProtocolProcessor.AddFrame(context, ref holder, frame);
     }
 }
Exemple #2
0
        public static WebSocketsFrame TryParseFrameHeader(byte[] buffer, int bytesAvailable, out int headerLength)
        {
            int maskOffset;
            if (bytesAvailable < 2)
            {
                headerLength = 0;
                return null;
            }
            bool masked = (buffer[1] & 128) != 0;
            int tmp = buffer[1] & 127;
            int payloadLen;
            switch (tmp)
            {
                case 126:
                    headerLength = masked ? 8 : 4;
                    if (bytesAvailable < headerLength) return null;
                    payloadLen = (buffer[2] << 8) | buffer[3];
                    maskOffset = 4;
                    break;
                case 127:
                    headerLength = masked ? 14 : 10;
                    if (bytesAvailable < headerLength) return null;
                    int big = WebSocketsProcessor.ReadInt32(buffer, 2), little = WebSocketsProcessor.ReadInt32(buffer, 6);
                    if (big != 0 || little < 0) throw new ArgumentOutOfRangeException(); // seriously, we're not going > 2GB
                    payloadLen = little;
                    maskOffset = 10;
                    break;
                default:
                    headerLength = masked ? 6 : 2;
                    if (bytesAvailable < headerLength) return null;
                    payloadLen = tmp;
                    maskOffset = 2;
                    break;
            }

            var frame = new WebSocketsFrame();

            frame.IsFinal = (buffer[0] & 128) != 0;
            frame.Reserved1 = (buffer[0] & 64) != 0;
            frame.Reserved2 = (buffer[0] & 32) != 0;
            frame.Reserved3 = (buffer[0] & 16) != 0;
            frame.OpCode = (WebSocketsFrame.OpCodes)(buffer[0] & 15);
            frame.Mask = masked ? (int?)WebSocketsProcessor.ReadInt32(buffer, maskOffset) : null;
            frame.PayloadLength = payloadLen;

            return frame;
        }
Exemple #3
0
        protected override void GracefulShutdown(NetContext context, Connection connection)
        {
            connection.DisableRead();
            var ws = (WebSocketConnection)connection;

            if (!ws.HasSentClose)
            {
                ws.HasSentClose = true;
                var close = new WebSocketsFrame();
                if (IsClient)
                {
                    close.Mask = CreateMask();
                }
                close.OpCode        = WebSocketsFrame.OpCodes.Close;
                close.PayloadLength = 0;
                SendControl(context, close);
            }
            SendShutdown(context);
            connection.PromptToSend(context);
        }
        private void ProcessFrame(NetContext context, Connection connection, WebSocketsFrame frame)
        {
            if(IsClient)
            {
                if(frame.Mask.HasValue) throw new InvalidOperationException("Clients only expect unmasked frames");
            } else
            {
                if (!frame.Mask.HasValue) throw new InvalidOperationException("Servers only expect masked frames");
            }
            var ws = (WebSocketConnection) connection;
            if(frame.IsControlFrame)
            {
                if(!frame.IsFinal) throw new InvalidOperationException("control frames cannot be fragmented");
                switch(frame.OpCode)
                {
                    case WebSocketsFrame.OpCodes.Close:
                        // respond with a closing handshake
                        GracefulShutdown(context, connection);
                        break;
                    case WebSocketsFrame.OpCodes.Pong:
                        // could add some reaction here
                        break;
                    case WebSocketsFrame.OpCodes.Ping:
                        // send a "pong" with the same payload
                        var pong = new WebSocketsFrame();
                        if (IsClient) pong.Mask = CreateMask();
                        pong.OpCode = WebSocketsFrame.OpCodes.Binary;
                        pong.PayloadLength = frame.PayloadLength;
                        if(frame.Payload != null)
                        {
                            var ms = new MemoryStream((int)frame.Payload.Length);
                            byte[] buffer = null;
                            try
                            {
                                buffer = context.GetBuffer();
                                Copy(frame.Payload, ms, buffer, null, frame.PayloadLength);
                            } finally
                            {
                                context.Recycle(buffer);
                            }
                            pong.Payload = ms;
                        }
                        SendControl(context, pong);
                        connection.PromptToSend(context);
                        break;
                    default:
                        throw new InvalidOperationException();
                }
            }
            else
            {
                if (!ws.AllowIncomingDataFrames) throw new InvalidOperationException("Data frames disabled");

                int pendingCount = FrameCount;
                if (frame.OpCode == WebSocketsFrame.OpCodes.Continuation)
                {
                    if(pendingCount == 0) throw new InvalidOperationException("Continuation of nothing");
                }
                else  if (pendingCount != 0)
                {
                    throw new InvalidOperationException("Should be a continuation");
                }
                if (!frame.IsFinal)
                {
                    Push(context, frame);
                }
                else
                {
                    if(pendingCount == 0)
                    {
                        Process(context, connection, frame.Payload, frame.OpCode);
                    } else
                    {
                        throw new NotImplementedException();
                    }
                }

            }
            if (frame.OpCode == WebSocketsFrame.OpCodes.Close)
            {
                Debug.WriteLine("Sent close frame: " + connection);
            }
        }
 protected override void GracefulShutdown(NetContext context, Connection connection)
 {
     connection.DisableRead();
     var ws = (WebSocketConnection) connection;
     if(!ws.HasSentClose)
     {
         ws.HasSentClose = true;
         var close = new WebSocketsFrame();
         if (IsClient) close.Mask = CreateMask();
         close.OpCode = WebSocketsFrame.OpCodes.Close;
         close.PayloadLength = 0;
         SendControl(context, close);
     }
     SendShutdown(context);
     connection.PromptToSend(context);
 }
 static string ReadFrameMessage(WebSocketsFrame frame)
 {
     var ms = new MemoryStream();
     frame.Payload.CopyTo(ms);
     return Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Length);
 }
        static string DecompressFrame(IExtension extn, params byte[] data)
        {
            var handler = new TcpHandler();
            var ctx = new NetContext(delegate { }, handler);
            
            if (extn == null) extn = PerFrameDeflate.Default.CreateExtension("deflate-frame");
            var frame = new WebSocketsFrame {
                OpCode = WebSocketsFrame.OpCodes.Text,
                Payload = new MemoryStream(data),
                PayloadLength = data.Length,
                Reserved1 = true
            };
            var connection = new WebSocketConnection(new IPEndPoint(IPAddress.Loopback, 20000));
            var decoded = extn.ApplyIncoming(ctx, connection, frame).Single();

            return ReadFrameMessage(decoded);
        }
Exemple #8
0
            IEnumerable <WebSocketsFrame> IExtension.ApplyOutgoing(NetContext context, WebSocketConnection connection, WebSocketsFrame frame)
            {
                if (!frame.IsControlFrame && frame.PayloadLength > parent.compressMessagesLargerThanBytes)
                {
                    if (frame.Reserved1)
                    {
                        throw new InvalidOperationException("Reserved1 flag is already set; extension conflict?");
                    }

                    int headerBytes = 0;
                    if (outbound == null)
                    {
                        outbound = new ZStream();
                        const int BITS = 12; // 4096 byte outbound buffer (instead of full 32k=15)
                        outbound.deflateInit(zlibConst.Z_BEST_COMPRESSION, BITS);
                        headerBytes = 2;
                    }
                    BufferStream tmp     = null;
                    var          payload = frame.Payload;
                    payload.Position = 0;
                    byte[] inBuffer = null, outBuffer = null;
                    try
                    {
                        inBuffer  = context.GetBuffer();
                        outBuffer = context.GetBuffer();
                        tmp       = new BufferStream(context, 0);

                        outbound.next_out = outBuffer;
                        outbound.next_in  = inBuffer;

                        int remaining = frame.PayloadLength;
                        while (remaining > 0)
                        {
                            int readCount = payload.Read(inBuffer, 0, inBuffer.Length);
                            if (readCount <= 0)
                            {
                                break;
                            }
                            remaining -= readCount;

                            outbound.next_in_index = 0;
                            outbound.avail_in      = readCount;

                            do
                            {
                                outbound.next_out_index = 0;
                                outbound.avail_out      = outBuffer.Length;
                                long priorOut = outbound.total_out;
                                int  err      = outbound.deflate(remaining == 0 ? zlibConst.Z_SYNC_FLUSH : zlibConst.Z_NO_FLUSH);
                                if (err != zlibConst.Z_OK && err != zlibConst.Z_STREAM_END)
                                {
                                    throw new ZStreamException("deflating: " + outbound.msg);
                                }

                                int outCount = (int)(outbound.total_out - priorOut);
                                if (outCount > 0)
                                {
                                    if (headerBytes == 0)
                                    {
                                        tmp.Write(outBuffer, 0, outCount);
                                    }
                                    else
                                    {
                                        if (outCount < headerBytes)
                                        {
                                            throw new InvalidOperationException("Failed to write entire header");
                                        }
                                        // check the generated header meets our expectations
                                        // CMF is very specific - CM must be 8, and CINFO must be <=7 (for 32k window)
                                        if ((outBuffer[0] & 15) != 8)
                                        {
                                            throw new InvalidOperationException("Zlib CM header was incorrect");
                                        }
                                        if ((outBuffer[0] & 128) != 0) // if msb set, is > 7 - invalid
                                        {
                                            throw new InvalidOperationException("Zlib CINFO header was incorrect");
                                        }

                                        // FLG is less important; FCHECK is irrelevent, FLEVEL doesn't matter; but
                                        // FDICT must be zero, to ensure that we aren't expecting a an initialization dictionary
                                        if ((outBuffer[1] & 32) != 0)
                                        {
                                            throw new InvalidOperationException("Zlib FLG.FDICT header was set (must not be)");
                                        }

                                        // skip the header, and write anything else
                                        outCount -= headerBytes;
                                        if (outCount > 0)
                                        {
                                            tmp.Write(outBuffer, headerBytes, outCount);
                                        }
                                        headerBytes = 0; // all written now
                                    }
                                }
                            } while (outbound.avail_in > 0 || outbound.avail_out == 0);
                        }
                        if (remaining != 0)
                        {
                            throw new EndOfStreamException();
                        }
                        if (headerBytes != 0)
                        {
                            throw new InvalidOperationException("Zlib header was not written");
                        }

                        // verify the last 4 bytes, then drop them
                        tmp.Position = tmp.Length - 4;
                        NetContext.Fill(tmp, outBuffer, 4);
                        if (!(outBuffer[0] == 0x00 && outBuffer[1] == 0x00 && outBuffer[2] == 0xFF && outBuffer[3] == 0xFF))
                        {
                            throw new InvalidOperationException("expectation failed: 0000FFFF in the tail");
                        }

                        if (parent.disableContextTakeover && tmp.Length >= frame.PayloadLength)
                        { // compressing it didn't do anything useful; since we're going to discard
                          // the compression context, we might as well stick with the original data
                            payload.Position = 0;
                            payload          = null; // so that it doesn't get disposed
                        }
                        else
                        {
                            // set our final output
                            tmp.Position = 0;
                            tmp.SetLength(tmp.Length - 4);
                            long bytesSaved = frame.PayloadLength - tmp.Length;
                            frame.Payload       = tmp;
                            frame.PayloadLength = (int)tmp.Length;
                            frame.Reserved1     = true;
                            tmp = payload as BufferStream;
                            parent.RegisterOutboundBytesSaved(bytesSaved);
                        }
                    }
#if DEBUG
                    catch (Exception ex)
                    {
                        Debug.WriteLine(ex);
                        throw;
                    }
#endif
                    finally
                    {
                        if (outbound != null)
                        {
                            outbound.next_out = null;
                            outbound.next_in  = null;
                        }
                        if (tmp != null)
                        {
                            tmp.Dispose();
                        }
                        if (inBuffer != null)
                        {
                            context.Recycle(inBuffer);
                        }
                        if (outBuffer != null)
                        {
                            context.Recycle(outBuffer);
                        }
                        if (parent.disableContextTakeover)
                        {
                            ClearContext(false, true);
                        }
                    }
                }
                yield return(frame);
            }
            IEnumerable<WebSocketsFrame> IExtension.ApplyOutgoing(NetContext context, WebSocketConnection connection, WebSocketsFrame frame)
            {
                if (!frame.IsControlFrame && frame.PayloadLength > parent.compressMessagesLargerThanBytes)
                {
                    if (frame.Reserved1)
                    {
                        throw new InvalidOperationException("Reserved1 flag is already set; extension conflict?");
                    }

                    int headerBytes = 0;
                    if (outbound == null)
                    {
                        outbound = new ZStream();
                        const int BITS = 12; // 4096 byte outbound buffer (instead of full 32k=15)
                        outbound.deflateInit(zlibConst.Z_BEST_COMPRESSION, BITS);
                        headerBytes = 2;
                    }
                    BufferStream tmp = null;
                    var payload = frame.Payload;
                    payload.Position = 0;
                    byte[] inBuffer = null, outBuffer = null;
                    try
                    {
                        inBuffer = context.GetBuffer();
                        outBuffer = context.GetBuffer();
                        tmp = new BufferStream(context, 0);

                        outbound.next_out = outBuffer;
                        outbound.next_in = inBuffer;

                        int remaining = frame.PayloadLength;
                        while (remaining > 0)
                        {
                            int readCount = payload.Read(inBuffer, 0, inBuffer.Length);
                            if (readCount <= 0) break;
                            remaining -= readCount;

                            outbound.next_in_index = 0;
                            outbound.avail_in = readCount;

                            do
                            {
                                outbound.next_out_index = 0;
                                outbound.avail_out = outBuffer.Length;
                                long priorOut = outbound.total_out;
                                int err = outbound.deflate(remaining == 0 ? zlibConst.Z_SYNC_FLUSH : zlibConst.Z_NO_FLUSH);
                                if (err != zlibConst.Z_OK && err != zlibConst.Z_STREAM_END)
                                    throw new ZStreamException("deflating: " + outbound.msg);

                                int outCount = (int)(outbound.total_out - priorOut);
                                if (outCount > 0)
                                {
                                    if (headerBytes == 0)
                                    {
                                        tmp.Write(outBuffer, 0, outCount);
                                    }
                                    else
                                    {
                                        if (outCount < headerBytes)
                                        {
                                            throw new InvalidOperationException("Failed to write entire header");
                                        }
                                        // check the generated header meets our expectations
                                        // CMF is very specific - CM must be 8, and CINFO must be <=7 (for 32k window)
                                        if ((outBuffer[0] & 15) != 8)
                                        {
                                            throw new InvalidOperationException("Zlib CM header was incorrect");
                                        }
                                        if ((outBuffer[0] & 128) != 0) // if msb set, is > 7 - invalid
                                        {
                                            throw new InvalidOperationException("Zlib CINFO header was incorrect");
                                        }

                                        // FLG is less important; FCHECK is irrelevent, FLEVEL doesn't matter; but
                                        // FDICT must be zero, to ensure that we aren't expecting a an initialization dictionary
                                        if ((outBuffer[1] & 32) != 0)
                                        {
                                            throw new InvalidOperationException("Zlib FLG.FDICT header was set (must not be)");
                                        }

                                        // skip the header, and write anything else
                                        outCount -= headerBytes;
                                        if (outCount > 0)
                                        {
                                            tmp.Write(outBuffer, headerBytes, outCount);
                                        }
                                        headerBytes = 0; // all written now
                                    }
                                }
                            } while (outbound.avail_in > 0 || outbound.avail_out == 0);
                        }
                        if (remaining != 0) throw new EndOfStreamException();
                        if (headerBytes != 0) throw new InvalidOperationException("Zlib header was not written");

                        // verify the last 4 bytes, then drop them
                        tmp.Position = tmp.Length - 4;
                        NetContext.Fill(tmp, outBuffer, 4);
                        if (!(outBuffer[0] == 0x00 && outBuffer[1] == 0x00 && outBuffer[2] == 0xFF && outBuffer[3] == 0xFF))
                        {
                            throw new InvalidOperationException("expectation failed: 0000FFFF in the tail");
                        }

                        if (parent.disableContextTakeover && tmp.Length >= frame.PayloadLength)
                        { // compressing it didn't do anything useful; since we're going to discard
                          // the compression context, we might as well stick with the original data
                            payload.Position = 0;
                            payload = null; // so that it doesn't get disposed
                        }
                        else
                        {
                            // set our final output
                            tmp.Position = 0;
                            tmp.SetLength(tmp.Length - 4);
                            long bytesSaved = frame.PayloadLength - tmp.Length;
                            frame.Payload = tmp;
                            frame.PayloadLength = (int)tmp.Length;
                            frame.Reserved1 = true;
                            tmp = payload as BufferStream;
                            parent.RegisterOutboundBytesSaved(bytesSaved);
                        }
                    }
#if DEBUG
                    catch (Exception ex)
                    {
                        Debug.WriteLine(ex);
                        throw;
                    }
#endif
                    finally
                    {
                        if (outbound != null)
                        {
                            outbound.next_out = null;
                            outbound.next_in = null;
                        }
                        if (tmp != null) tmp.Dispose();
                        if (inBuffer != null) context.Recycle(inBuffer);
                        if (outBuffer != null) context.Recycle(outBuffer);
                        if (parent.disableContextTakeover) ClearContext(false, true);
                    }
                }
                yield return frame;
            }
        private static IEnumerable<WebSocketsFrame> ApplyExtensions(NetContext context, Connection connection, WebSocketsFrame frame, bool inbound)
        {
            var wsc = connection as WebSocketConnection;
            var extn = wsc == null ? null : wsc.Extensions;

            IEnumerable<WebSocketsFrame> frames = Enumerable.Repeat(frame, 1);

            if (extn != null && extn.Length != 0)
            {
                for (int i = 0; i < extn.Length; i++)
                {
                    var actualExtension = extn[i];
                    if (inbound)
                    {
                        frames = frames.SelectMany(x => actualExtension.ApplyIncoming(context, wsc, x));
                    }
                    else
                    {
                        frames = frames.SelectMany(x => actualExtension.ApplyOutgoing(context, wsc, x));
                    }
                }
            }
            return frames;
        }
Exemple #11
0
        private static IEnumerable <WebSocketsFrame> ApplyExtensions(NetContext context, Connection connection, WebSocketsFrame frame, bool inbound)
        {
            var wsc  = connection as WebSocketConnection;
            var extn = wsc == null ? null : wsc.Extensions;

            IEnumerable <WebSocketsFrame> frames = Enumerable.Repeat(frame, 1);

            if (extn != null && extn.Length != 0)
            {
                for (int i = 0; i < extn.Length; i++)
                {
                    var actualExtension = extn[i];
                    if (inbound)
                    {
                        frames = frames.SelectMany(x => actualExtension.ApplyIncoming(context, wsc, x));
                    }
                    else
                    {
                        frames = frames.SelectMany(x => actualExtension.ApplyOutgoing(context, wsc, x));
                    }
                }
            }
            return(frames);
        }
Exemple #12
0
        public override void Send(NetContext context, Connection connection, object message)
        {
            if (message == null)
            {
                throw new ArgumentNullException("message");
            }

            string s;
            WebSocketConnection wsConnection;
            WebSocketsFrame     frame;

            var encoding = Encoding.UTF8;

            if ((s = message as string) != null && (wsConnection = connection as WebSocketConnection) != null &&
                wsConnection.MaxCharactersPerFrame > 0 && s.Length > wsConnection.MaxCharactersPerFrame)
            {
                int    remaining = s.Length, charIndex = 0;
                bool   isFirst    = true;
                char[] charBuffer = s.ToCharArray();
                var    batch      = new List <IFrame>();
                while (remaining > 0)
                {
                    int charCount = Math.Min(remaining, wsConnection.MaxCharactersPerFrame);
                    var buffer    = encoding.GetBytes(charBuffer, charIndex, charCount);
                    //Debug.WriteLine("> " + new string(charBuffer, charIndex, charCount));
                    remaining -= charCount;
                    charIndex += charCount;

                    frame         = new WebSocketsFrame();
                    frame.OpCode  = isFirst ? WebSocketsFrame.OpCodes.Text : WebSocketsFrame.OpCodes.Continuation;
                    isFirst       = false;
                    frame.Payload = new BufferStream(context, context.Handler.MaxOutgoingQuota);
                    frame.Payload.Write(buffer, 0, buffer.Length);
                    frame.PayloadLength    = buffer.Length;
                    frame.Mask             = IsClient ? (int?)CreateMask() : null;
                    frame.Payload.Position = 0;
                    frame.IsFinal          = remaining <= 0;
                    foreach (var final in ApplyExtensions(context, connection, frame, false))
                    {
                        batch.Add(final);
                    }
                }
                SendData(context, batch);
                return;
            }


            byte[] blob;
            frame         = new WebSocketsFrame();
            frame.Payload = new BufferStream(context, context.Handler.MaxOutgoingQuota);
            if (s != null)
            {
                frame.OpCode = WebSocketsFrame.OpCodes.Text;
                var buffer = encoding.GetBytes(s);
                //Debug.WriteLine("> " + s);
                frame.Payload.Write(buffer, 0, buffer.Length);
                frame.PayloadLength = buffer.Length;
            }
            else if ((blob = message as byte[]) != null)
            {
                frame.OpCode = WebSocketsFrame.OpCodes.Binary;
                frame.Payload.Write(blob, 0, blob.Length);
                frame.PayloadLength = blob.Length;
            }
            else
            {
                throw new NotSupportedException(message.ToString());
            }
            frame.Mask             = IsClient ? (int?)CreateMask() : null;
            frame.Payload.Position = 0;
            frame.IsFinal          = true;

            foreach (var final in ApplyExtensions(context, connection, frame, false))
            {
                SendData(context, final);
            }
        }
Exemple #13
0
        private void ProcessFrame(NetContext context, Connection connection, WebSocketsFrame frame)
        {
            if (IsClient)
            {
                if (frame.Mask.HasValue)
                {
                    throw new InvalidOperationException("Clients only expect unmasked frames");
                }
            }
            else
            {
                if (!frame.Mask.HasValue)
                {
                    throw new InvalidOperationException("Servers only expect masked frames");
                }
            }
            var ws = (WebSocketConnection)connection;

            if (frame.IsControlFrame)
            {
                if (!frame.IsFinal)
                {
                    throw new InvalidOperationException("control frames cannot be fragmented");
                }
                switch (frame.OpCode)
                {
                case WebSocketsFrame.OpCodes.Close:
                    // respond with a closing handshake
                    GracefulShutdown(context, connection);
                    break;

                case WebSocketsFrame.OpCodes.Pong:
                    // could add some reaction here
                    break;

                case WebSocketsFrame.OpCodes.Ping:
                    // send a "pong" with the same payload
                    var pong = new WebSocketsFrame();
                    if (IsClient)
                    {
                        pong.Mask = CreateMask();
                    }
                    pong.OpCode        = WebSocketsFrame.OpCodes.Binary;
                    pong.PayloadLength = frame.PayloadLength;
                    if (frame.Payload != null)
                    {
                        var    ms     = new MemoryStream((int)frame.Payload.Length);
                        byte[] buffer = null;
                        try
                        {
                            buffer = context.GetBuffer();
                            Copy(frame.Payload, ms, buffer, null, frame.PayloadLength);
                        } finally
                        {
                            context.Recycle(buffer);
                        }
                        pong.Payload = ms;
                    }
                    SendControl(context, pong);
                    connection.PromptToSend(context);
                    break;

                default:
                    throw new InvalidOperationException();
                }
            }
            else
            {
                if (!ws.AllowIncomingDataFrames)
                {
                    throw new InvalidOperationException("Data frames disabled");
                }

                int pendingCount = FrameCount;
                if (frame.OpCode == WebSocketsFrame.OpCodes.Continuation)
                {
                    if (pendingCount == 0)
                    {
                        throw new InvalidOperationException("Continuation of nothing");
                    }
                }
                else if (pendingCount != 0)
                {
                    throw new InvalidOperationException("Should be a continuation");
                }
                if (!frame.IsFinal)
                {
                    Push(context, frame);
                }
                else
                {
                    if (pendingCount == 0)
                    {
                        Process(context, connection, frame.Payload, frame.OpCode);
                    }
                    else
                    {
                        throw new NotImplementedException();
                    }
                }
            }
            if (frame.OpCode == WebSocketsFrame.OpCodes.Close)
            {
                Debug.WriteLine("Sent close frame: " + connection);
            }
        }
Exemple #14
0
        protected override int ProcessIncoming(NetContext context, Connection connection, Stream incoming)
        {
            if (incoming.Length < 2)
            {
                return(0);                     // can't read that; frame takes at minimum two bytes
            }
            byte[] buffer = null;
            try
            {
                buffer = context.GetBuffer();
                int read;
                // read as much as possible up to 14 bytes; that gives us space for 2 bytes frame header, 8 bytes length, and 4 bytes mask
                read = NetContext.TryFill(incoming, buffer, 14);

                int headerLength;
                var frame = WebSocketsFrame.TryParseFrameHeader(buffer, read, out headerLength);

                if (frame == null)
                {
                    return(0);
                }
                int payloadLen = frame.PayloadLength;

#if VERBOSE
                Debug.WriteLine("Parsed header from: " + BitConverter.ToString(buffer, 0, headerLength));
#endif

                if (incoming.Length < headerLength + payloadLen)
                {
                    return(0);                                             // not enough data to read the payload
                }
                if (payloadLen != 0)
                {
                    Stream payload = null;
                    try
                    {
                        payload = new BufferStream(context, context.Handler.MaxIncomingQuota);
                        if (read != headerLength)
                        {
                            incoming.Position -= (read - headerLength);                       // we got it wrong...
                        }
                        Copy(incoming, payload, buffer, frame.Mask, payloadLen);

                        if (payloadLen != payload.Length)
                        {
                            throw new InvalidOperationException("I munged the data");
                        }
                        frame.Payload = payload;
                        payload       = null;
                    }
                    finally
                    {
                        if (payload != null)
                        {
                            payload.Dispose();
                        }
                    }
                }
                foreach (var final in ApplyExtensions(context, connection, frame, true))
                {
                    ProcessFrame(context, connection, final);
                }
                return(headerLength + payloadLen);
            }
            finally
            {
                context.Recycle(buffer);
            }
        }
Exemple #15
0
            IEnumerable <WebSocketsFrame> IExtension.ApplyIncoming(NetContext context, WebSocketConnection connection, WebSocketsFrame frame)
            {
                if (frame.Reserved1 && !frame.IsControlFrame)
                {
                    BufferStream tmp     = null;
                    var          payload = frame.Payload;
                    payload.Position = 0;
                    byte[] inBuffer = null, outBuffer = null;

                    try
                    {
                        outBuffer = context.GetBuffer();
                        inBuffer  = context.GetBuffer();
                        tmp       = new BufferStream(context, 0);

                        if (inbound == null)
                        {
                            inbound = new ZStream();
                            inbound.inflateInit();

                            // fake a zlib header with:
                            // CMF:
                            //   CM = 8 (deflate)
                            //   CINFO = 7 (32k window)
                            // FLG:
                            //   FCHECK: 26 (checksum of other bits)
                            //   FDICT: 0 (no dictionary)
                            //   FLEVEL: 3 (maximum)
                            inBuffer[0]     = 120;
                            inBuffer[1]     = 218;
                            inbound.next_in = inBuffer;
                            int chk = Inflate(tmp, outBuffer, 2);
                            if (chk != 0)
                            {
                                throw new InvalidOperationException("Spoofed zlib header suggested data");
                            }
                        }

                        inbound.next_in = inBuffer;
                        int remaining = frame.PayloadLength;
                        //bool first = true;
                        while (remaining > 0)
                        {
                            int readCount = payload.Read(inBuffer, 0, inBuffer.Length);
                            if (readCount <= 0)
                            {
                                break;
                            }
                            remaining -= readCount;

                            //if (first)
                            //{   // kill the BFINAL flag from the first block, if set; we don't want zlib
                            //    // trying to verify the ADLER checksum; unfortunately, a frame can contain
                            //    // multiple blocks, and a *later* block could have BFINAL set. That sucks.
                            //    inBuffer[0] &= 254;
                            //    first = false;
                            //}
                            Inflate(tmp, outBuffer, readCount);
                        }
                        if (remaining != 0)
                        {
                            throw new EndOfStreamException();
                        }

                        // spoof the missing 4 bytes from the tail
                        inBuffer[0] = inBuffer[1] = 0x00;
                        inBuffer[2] = inBuffer[3] = 0xFF;
                        Inflate(tmp, outBuffer, 4);

                        // set our final output
                        tmp.Position  = 0;
                        frame.Payload = tmp;
                        long bytesSaved = tmp.Length - frame.PayloadLength;
                        frame.PayloadLength = (int)tmp.Length;
                        frame.Reserved1     = false;
                        tmp = payload as BufferStream;
                        parent.RegisterInboundBytesSaved(bytesSaved);
                    }
#if DEBUG
                    catch (Exception ex)
                    {
                        Debug.WriteLine(ex);
                        throw;
                    }
#endif
                    finally
                    {
                        if (inbound != null)
                        {
                            inbound.next_out = null;
                            inbound.next_in  = null;
                        }
                        if (tmp != null)
                        {
                            tmp.Dispose();
                        }
                        if (inBuffer != null)
                        {
                            context.Recycle(inBuffer);
                        }
                        if (outBuffer != null)
                        {
                            context.Recycle(outBuffer);
                        }
                        if (parent.disableContextTakeover)
                        {
                            ClearContext(true, false);
                        }
                    }
                }
                yield return(frame);
            }
 private void Process(NetContext context, Connection connection, Stream stream, WebSocketsFrame.OpCodes opCode)
 {
     using (stream)
     {
         switch (opCode)
         {
             case WebSocketsFrame.OpCodes.Text:
                 string s;
                 if (stream == null || stream.Length == 0) s = "";
                 else
                 {
                     stream.Position = 0;
                     using (var reader = new StreamReader(stream, Encoding.UTF8))
                     {
                         s = reader.ReadToEnd();
                     }
                 }
                 context.Handler.OnReceived(connection, s);
                 break;
             case WebSocketsFrame.OpCodes.Binary:
                 byte[] blob;
                 int len;
                 if (stream == null || (len = ((int)stream.Length - (int)stream.Position)) == 0) blob = new byte[0]; 
                 else
                 {
                     blob = new byte[len];
                     byte[] buffer = null;
                     try
                     {
                         buffer = context.GetBuffer();
                         int offset = 0, read;
                         stream.Position = 0;
                         while((read = stream.Read(buffer, offset, Math.Min(len, buffer.Length))) > 0)
                         {
                             offset += read;
                             len -= read;
                         }
                         if(len != 0) throw new EndOfStreamException();
                     } finally
                     {
                         context.Recycle(buffer);
                     }
                 }
                 context.Handler.OnReceived(connection, blob);
                 break;
             default:
                 throw new InvalidOperationException();
         }
     }
 }
        public override void Send(NetContext context, Connection connection, object message)
        {
            if(message == null) throw new ArgumentNullException("message");

            string s;
            WebSocketConnection wsConnection;
            WebSocketsFrame frame;

            var encoding = Encoding.UTF8;
            if ((s = message as string) != null && (wsConnection = connection as WebSocketConnection) != null
                && wsConnection.MaxCharactersPerFrame > 0 && s.Length > wsConnection.MaxCharactersPerFrame)
            {
                int remaining = s.Length, charIndex = 0;
                bool isFirst = true;
                char[] charBuffer = s.ToCharArray();
                var batch = new List<IFrame>();
                while(remaining > 0)
                {
                    int charCount = Math.Min(remaining, wsConnection.MaxCharactersPerFrame);
                    var buffer = encoding.GetBytes(charBuffer, charIndex, charCount);
                    //Debug.WriteLine("> " + new string(charBuffer, charIndex, charCount));
                    remaining -= charCount;
                    charIndex += charCount;

                    frame = new WebSocketsFrame();
                    frame.OpCode = isFirst ? WebSocketsFrame.OpCodes.Text : WebSocketsFrame.OpCodes.Continuation;
                    isFirst = false;
                    frame.Payload = new BufferStream(context, context.Handler.MaxOutgoingQuota);
                    frame.Payload.Write(buffer, 0, buffer.Length);
                    frame.PayloadLength = buffer.Length;
                    frame.Mask = IsClient ? (int?)CreateMask() : null;
                    frame.Payload.Position = 0;
                    frame.IsFinal = remaining <= 0;
                    foreach (var final in ApplyExtensions(context, connection, frame, false))
                    {
                        batch.Add(final);
                    }                    
                }
                SendData(context, batch);
                return;
            }            


            byte[] blob;
            frame = new WebSocketsFrame();
            frame.Payload = new BufferStream(context, context.Handler.MaxOutgoingQuota);
            if(s != null)
            {
                frame.OpCode = WebSocketsFrame.OpCodes.Text;
                var buffer = encoding.GetBytes(s);
                //Debug.WriteLine("> " + s);
                frame.Payload.Write(buffer, 0, buffer.Length);
                frame.PayloadLength = buffer.Length;
            }
            else if ((blob = message as byte[]) != null)
            {
                frame.OpCode = WebSocketsFrame.OpCodes.Binary;
                frame.Payload.Write(blob, 0, blob.Length);
                frame.PayloadLength = blob.Length;
            }
            else
            {
                throw new NotSupportedException(message.ToString());
            }
            frame.Mask = IsClient ? (int?)CreateMask() : null;
            frame.Payload.Position = 0;
            frame.IsFinal = true;

            foreach(var final in ApplyExtensions(context, connection, frame, false))
            {
                SendData(context, final);
            }
        }
 protected void Push(NetContext context, WebSocketsFrame frame)
 {
     lock (this)
     {
         int maxQuota = context.Handler.MaxIncomingQuota;
         if (maxQuota > 0 && ProtocolProcessor.Sum(holder, f =>
         {
             var typed = f as WebSocketsFrame;
             return typed == null ? 0 : typed.GetLengthEstimate();
         }) + frame.GetLengthEstimate() > maxQuota)
         {
             throw new InvalidOperationException("Inbound quota exceeded");
         }
         ProtocolProcessor.AddFrame(context, ref holder, frame);
     }
 }
        public static WebSocketsFrame TryParseFrameHeader(byte[] buffer, int bytesAvailable, out int headerLength)
        {
            int maskOffset;

            if (bytesAvailable < 2)
            {
                headerLength = 0;
                return(null);
            }
            bool masked = (buffer[1] & 128) != 0;
            int  tmp    = buffer[1] & 127;
            int  payloadLen;

            switch (tmp)
            {
            case 126:
                headerLength = masked ? 8 : 4;
                if (bytesAvailable < headerLength)
                {
                    return(null);
                }
                payloadLen = (buffer[2] << 8) | buffer[3];
                maskOffset = 4;
                break;

            case 127:
                headerLength = masked ? 14 : 10;
                if (bytesAvailable < headerLength)
                {
                    return(null);
                }
                int big = WebSocketsProcessor.ReadInt32(buffer, 2), little = WebSocketsProcessor.ReadInt32(buffer, 6);
                if (big != 0 || little < 0)
                {
                    throw new ArgumentOutOfRangeException();                             // seriously, we're not going > 2GB
                }
                payloadLen = little;
                maskOffset = 10;
                break;

            default:
                headerLength = masked ? 6 : 2;
                if (bytesAvailable < headerLength)
                {
                    return(null);
                }
                payloadLen = tmp;
                maskOffset = 2;
                break;
            }

            var frame = new WebSocketsFrame();

            frame.IsFinal       = (buffer[0] & 128) != 0;
            frame.Reserved1     = (buffer[0] & 64) != 0;
            frame.Reserved2     = (buffer[0] & 32) != 0;
            frame.Reserved3     = (buffer[0] & 16) != 0;
            frame.OpCode        = (WebSocketsFrame.OpCodes)(buffer[0] & 15);
            frame.Mask          = masked ? (int?)WebSocketsProcessor.ReadInt32(buffer, maskOffset) : null;
            frame.PayloadLength = payloadLen;

            return(frame);
        }
        public void CompressFrame()
        {
            var handler = new TcpHandler();
            var ctx = new NetContext(delegate { }, handler);

            IExtensionFactory factory = new PerFrameDeflate(0, false);
            var extn = factory.CreateExtension("deflate-frame");
            var data = Encoding.UTF8.GetBytes("Hello");
            var frame = new WebSocketsFrame
            {
                OpCode = WebSocketsFrame.OpCodes.Text,
                Payload = new MemoryStream(data),
                PayloadLength = data.Length,
                Reserved1 = false
            };
            var connection = new WebSocketConnection(new IPEndPoint(IPAddress.Loopback, 20000));
            var encoded = extn.ApplyOutgoing(ctx, connection, frame).Single();
            var ms = new MemoryStream();
            encoded.Payload.CopyTo(ms);
            string hex = BitConverter.ToString(ms.GetBuffer(), 0, (int)ms.Length);
            Assert.AreEqual("F2-48-CD-C9-C9-07-00", hex);

            // unrelated decoder
            extn = PerFrameDeflate.Default.CreateExtension("deflate-frame");
            var decoded = extn.ApplyIncoming(ctx, connection, frame).Single();

            ms = new MemoryStream();
            decoded.Payload.Position = 0;
            decoded.Payload.CopyTo(ms);
            string s = Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Length);
            Assert.AreEqual("Hello", s);
        }
            IEnumerable<WebSocketsFrame> IExtension.ApplyIncoming(NetContext context, WebSocketConnection connection, WebSocketsFrame frame)
            {
                if (frame.Reserved1 && !frame.IsControlFrame)
                {
                    BufferStream tmp = null;
                    var payload = frame.Payload;
                    payload.Position = 0;
                    byte[] inBuffer = null, outBuffer = null;

                    try
                    {
                        outBuffer = context.GetBuffer();
                        inBuffer = context.GetBuffer();
                        tmp = new BufferStream(context, 0);

                        if (inbound == null)
                        {
                            inbound = new ZStream();
                            inbound.inflateInit();

                            // fake a zlib header with:
                            // CMF:
                            //   CM = 8 (deflate)
                            //   CINFO = 7 (32k window)
                            // FLG:
                            //   FCHECK: 26 (checksum of other bits)
                            //   FDICT: 0 (no dictionary)
                            //   FLEVEL: 3 (maximum)
                            inBuffer[0] = 120;
                            inBuffer[1] = 218;
                            inbound.next_in = inBuffer;
                            int chk = Inflate(tmp, outBuffer, 2);
                            if (chk != 0) throw new InvalidOperationException("Spoofed zlib header suggested data");
                        }   
                        
                        inbound.next_in = inBuffer;
                        int remaining = frame.PayloadLength;
                        //bool first = true;
                        while (remaining > 0)
                        {
                            int readCount = payload.Read(inBuffer, 0, inBuffer.Length);
                            if (readCount <= 0) break;
                            remaining -= readCount;

                            //if (first)
                            //{   // kill the BFINAL flag from the first block, if set; we don't want zlib
                            //    // trying to verify the ADLER checksum; unfortunately, a frame can contain
                            //    // multiple blocks, and a *later* block could have BFINAL set. That sucks.
                            //    inBuffer[0] &= 254;
                            //    first = false;
                            //}
                            Inflate(tmp, outBuffer, readCount);                          
                        }
                        if (remaining != 0) throw new EndOfStreamException();

                        // spoof the missing 4 bytes from the tail
                        inBuffer[0] = inBuffer[1] = 0x00;
                        inBuffer[2] = inBuffer[3] = 0xFF;
                        Inflate(tmp, outBuffer, 4);

                        // set our final output
                        tmp.Position = 0;
                        frame.Payload = tmp;
                        long bytesSaved = tmp.Length - frame.PayloadLength;
                        frame.PayloadLength = (int)tmp.Length;
                        frame.Reserved1 = false;
                        tmp = payload as BufferStream;
                        parent.RegisterInboundBytesSaved(bytesSaved);
                    }
#if DEBUG
                    catch (Exception ex)
                    {
                        Debug.WriteLine(ex);
                        throw;
                    }
#endif
                    finally
                    {
                        if (inbound != null)
                        {
                            inbound.next_out = null;
                            inbound.next_in = null;
                        }
                        if (tmp != null) tmp.Dispose();
                        if (inBuffer != null) context.Recycle(inBuffer);
                        if (outBuffer != null) context.Recycle(outBuffer);
                        if (parent.disableContextTakeover) ClearContext(true, false);
                    }

                }
                yield return frame;
            }
 static WebSocketsFrame CreateFrame(string text)
 {
     var bytes = Encoding.UTF8.GetBytes(text);
     var frame = new WebSocketsFrame { OpCode = WebSocketsFrame.OpCodes.Text, Payload = new MemoryStream(bytes), PayloadLength = bytes.Length };
     return frame;
 }