protected override int ProcessIncoming(NetContext context, Connection connection, Stream incomingBuffer)
        {
            int len = FindHeadersLength(incomingBuffer);
            if (len <= 0) return 0;

            incomingBuffer.Position = 0;
            int extraConsumed;
            if (len < NetContext.BufferSize)
            {
                byte[] buffer = null;
                try
                {
                    buffer = context.GetBuffer();
                    NetContext.Fill(incomingBuffer, buffer, len);
                    using (var ms = new MemoryStream(buffer, 0, len))
                    {
                        extraConsumed = ProcessHeadersAndUpgrade(context, connection, ms, incomingBuffer, len);
                    }
                }
                finally
                {
                    context.Recycle(buffer);
                }
            }
            else
            {
                using (var ss = new SubStream(incomingBuffer, len))
                {
                    extraConsumed = ProcessHeadersAndUpgrade(context, connection, ss, incomingBuffer, len);
                }
            }
            if (extraConsumed < 0) return 0; // means: wasn't usable (yet)

            return len + extraConsumed;
        }
        }                                                              // doesn't need to be 100% accurate

        void IFrame.Write(NetContext context, Connection connection, Stream stream)
        {
            //Debug.WriteLine("write:" + this);
            byte[] buffer = null;
            try
            {
                buffer = context.GetBuffer();
                int offset = 0;
                buffer[offset++] = (byte)(((int)flags & 240) | ((int)OpCode & 15));
                if (PayloadLength > ushort.MaxValue)
                { // write as a 64-bit length
                    buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | 127);
                    buffer[offset++] = 0;
                    buffer[offset++] = 0;
                    buffer[offset++] = 0;
                    buffer[offset++] = 0;
                    buffer[offset++] = (byte)(PayloadLength >> 24);
                    buffer[offset++] = (byte)(PayloadLength >> 16);
                    buffer[offset++] = (byte)(PayloadLength >> 8);
                    buffer[offset++] = (byte)(PayloadLength);
                }
                else if (PayloadLength > 125)
                { // write as a 16-bit length
                    buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | 126);
                    buffer[offset++] = (byte)(PayloadLength >> 8);
                    buffer[offset++] = (byte)(PayloadLength);
                }
                else
                { // write in the header
                    buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | PayloadLength);
                }
                if (Mask.HasValue)
                {
                    int mask = Mask.Value;
                    buffer[offset++] = (byte)(mask >> 24);
                    buffer[offset++] = (byte)(mask >> 16);
                    buffer[offset++] = (byte)(mask >> 8);
                    buffer[offset++] = (byte)(mask);
                }
                stream.Write(buffer, 0, offset);

                if (PayloadLength != 0)
                {
                    int totalRead = WebSocketsProcessor_RFC6455_13.Copy(Payload, stream, buffer, Mask, PayloadLength);
                    if (totalRead != PayloadLength)
                    {
                        throw new EndOfStreamException("Wrong payload length sent");
                    }
                }
            }
            finally
            {
                context.Recycle(buffer);
            }
        }
        protected override int ProcessIncoming(NetContext context, Connection connection, Stream incomingBuffer)
        {
            int len = FindHeadersLength(incomingBuffer);

            if (len <= 0)
            {
                return(0);
            }

            incomingBuffer.Position = 0;
            int extraConsumed;

            if (len < NetContext.BufferSize)
            {
                byte[] buffer = null;
                try
                {
                    buffer = context.GetBuffer();
                    NetContext.Fill(incomingBuffer, buffer, len);
                    using (var ms = new MemoryStream(buffer, 0, len))
                    {
                        extraConsumed = ProcessHeadersAndUpgrade(context, connection, ms, incomingBuffer, len);
                    }
                }
                finally
                {
                    context.Recycle(buffer);
                }
            }
            else
            {
                using (var ss = new SubStream(incomingBuffer, len))
                {
                    extraConsumed = ProcessHeadersAndUpgrade(context, connection, ss, incomingBuffer, len);
                }
            }
            if (extraConsumed < 0)
            {
                return(0);                   // means: wasn't usable (yet)
            }
            return(len + extraConsumed);
        }
Exemple #4
0
        internal static string ComputeReply(NetContext context, string webSocketKey)
        {
            //To prove that the handshake was received, the server has to take two
            //pieces of information and combine them to form a response.  The first
            //piece of information comes from the |Sec-WebSocket-Key| header field
            //in the client handshake:

            //     Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

            //For this header field, the server has to take the value (as present
            //in the header field, e.g., the base64-encoded [RFC4648] version minus
            //any leading and trailing whitespace) and concatenate this with the
            //Globally Unique Identifier (GUID, [RFC4122]) "258EAFA5-E914-47DA-
            //95CA-C5AB0DC85B11" in string form, which is unlikely to be used by
            //network endpoints that do not understand the WebSocket Protocol.  A
            //SHA-1 hash (160 bits) [FIPS.180-3], base64-encoded (see Section 4 of
            //[RFC4648]), of this concatenation is then returned in the server's
            //handshake.
            if (string.IsNullOrEmpty(webSocketKey) || webSocketKey.Length != 24)
            {
                throw new ArgumentException("webSocketKey");
            }

            const string WebSocketKeySuffix = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

            byte[] buffer = null;
            try
            {
                buffer = context.GetBuffer();
                int offset = Encoding.ASCII.GetBytes(webSocketKey, 0, webSocketKey.Length, buffer, 0);
                int len    = offset + Encoding.ASCII.GetBytes(WebSocketKeySuffix, 0, WebSocketKeySuffix.Length, buffer, offset);
                using (var sha = SHA1.Create())
                {
                    return(Convert.ToBase64String(sha.ComputeHash(buffer, 0, len)));
                }
            }
            finally
            {
                context.Recycle(buffer);
            }
        }
        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);
            }
        }
        internal static string ComputeReply(NetContext context, string webSocketKey)
        {
            //To prove that the handshake was received, the server has to take two
            //pieces of information and combine them to form a response.  The first
            //piece of information comes from the |Sec-WebSocket-Key| header field
            //in the client handshake:

            //     Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

            //For this header field, the server has to take the value (as present
            //in the header field, e.g., the base64-encoded [RFC4648] version minus
            //any leading and trailing whitespace) and concatenate this with the
            //Globally Unique Identifier (GUID, [RFC4122]) "258EAFA5-E914-47DA-
            //95CA-C5AB0DC85B11" in string form, which is unlikely to be used by
            //network endpoints that do not understand the WebSocket Protocol.  A
            //SHA-1 hash (160 bits) [FIPS.180-3], base64-encoded (see Section 4 of
            //[RFC4648]), of this concatenation is then returned in the server's
            //handshake.
            if (string.IsNullOrEmpty(webSocketKey) || webSocketKey.Length != 24) throw new ArgumentException("webSocketKey");
            
            const string WebSocketKeySuffix = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
            byte[] buffer = null;
            try
            {
                buffer = context.GetBuffer();
                int offset = Encoding.ASCII.GetBytes(webSocketKey, 0, webSocketKey.Length, buffer, 0);
                int len = offset + Encoding.ASCII.GetBytes(WebSocketKeySuffix, 0, WebSocketKeySuffix.Length, buffer, offset);
                using(var sha = SHA1.Create())
                {
                    return Convert.ToBase64String(sha.ComputeHash(buffer, 0, len));
                }
            }
            finally
            {
                context.Recycle(buffer);
            }
        }
 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();
         }
     }
 }
        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 int ProcessIncoming(NetContext context, Connection connection, System.IO.Stream incoming)
        {
            if (incoming.Length < 2) return 0; // can't read that; start/end markers take at least 2 bytes

            switch(incoming.ReadByte())
            {
                case 0x00:
                    var ws = (WebSocketConnection) connection;
                    if(!ws.AllowIncomingDataFrames) throw new InvalidOperationException("Data frames disabled");
                    // data frame is 0x00 [data] 0xFF
                    int len = 0, cur;
                    while((cur = incoming.ReadByte()) != 0xFF && cur >= 0)
                    {
                        len++;
                    }
                    if(cur < 0) throw new EndOfStreamException();

                    incoming.Position = 1;
                    string value;
                    if (len == 0) value = "";
                    else if (len <= NetContext.BufferSize)
                    {
                        byte[] buffer = null;
                        try
                        {
                            buffer = context.GetBuffer();
                            NetContext.Fill(incoming, buffer, len);
                            value = Encoding.UTF8.GetString(buffer, 0, len);
                        } finally
                        {
                            context.Recycle(buffer);
                        }
                    } else
                    {
                        var buffer = new byte[len];
                        NetContext.Fill(incoming, buffer, len);
                        value = Encoding.UTF8.GetString(buffer, 0, len);
                    }
                    context.Handler.OnReceived(connection, value);
                    return len + 2;
                case 0xFF:
                    // shutdown is 0xFF 0x00
                    if(incoming.ReadByte() == 0x00)
                    {
                        GracefulShutdown(context, connection);
                        return 2;
                    } else
                    {
                        throw new InvalidOperationException("protocol fail");
                    }
                default:
                    throw new InvalidOperationException("protocol fail");
            }
        }
Exemple #10
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);
            }
Exemple #11
0
        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();
                }
            }
        }
Exemple #12
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 #13
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 #14
0
        internal int GetLengthEstimate() { return PayloadLength + 2; } // doesn't need to be 100% accurate
        void IFrame.Write(NetContext context, Connection connection, Stream stream)
        {
            //Debug.WriteLine("write:" + this);
            byte[] buffer = null;
            try
            {
                buffer = context.GetBuffer();
                int offset = 0;
                buffer[offset++] = (byte)(((int)flags & 240) | ((int)OpCode & 15));
                if (PayloadLength > ushort.MaxValue)
                { // write as a 64-bit length
                    buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | 127);
                    buffer[offset++] = 0;
                    buffer[offset++] = 0;
                    buffer[offset++] = 0;
                    buffer[offset++] = 0;
                    buffer[offset++] = (byte)(PayloadLength >> 24);
                    buffer[offset++] = (byte)(PayloadLength >> 16);
                    buffer[offset++] = (byte)(PayloadLength >> 8);
                    buffer[offset++] = (byte)(PayloadLength);
                }
                else if (PayloadLength > 125)
                { // write as a 16-bit length
                    buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | 126);
                    buffer[offset++] = (byte)(PayloadLength >> 8);
                    buffer[offset++] = (byte)(PayloadLength);
                }
                else
                { // write in the header
                    buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | PayloadLength);
                }
                if (Mask.HasValue)
                {
                    int mask = Mask.Value;
                    buffer[offset++] = (byte)(mask >> 24);
                    buffer[offset++] = (byte)(mask >> 16);
                    buffer[offset++] = (byte)(mask >> 8);
                    buffer[offset++] = (byte)(mask);
                }
                stream.Write(buffer, 0, offset);

                if (PayloadLength != 0)
                {
                    int totalRead = WebSocketsProcessor_RFC6455_13.Copy(Payload, stream, buffer, Mask, PayloadLength);
                    if (totalRead != PayloadLength)
                    {
                        throw new EndOfStreamException("Wrong payload length sent");
                    }
                }

            }
            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);
            }
            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.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;
            }
        protected override int ProcessIncoming(NetContext context, Connection connection, System.IO.Stream incoming)
        {
            if (incoming.Length < 2)
            {
                return(0);                     // can't read that; start/end markers take at least 2 bytes
            }
            switch (incoming.ReadByte())
            {
            case 0x00:
                var ws = (WebSocketConnection)connection;
                if (!ws.AllowIncomingDataFrames)
                {
                    throw new InvalidOperationException("Data frames disabled");
                }
                // data frame is 0x00 [data] 0xFF
                int len = 0, cur;
                while ((cur = incoming.ReadByte()) != 0xFF && cur >= 0)
                {
                    len++;
                }
                if (cur < 0)
                {
                    throw new EndOfStreamException();
                }

                incoming.Position = 1;
                string value;
                if (len == 0)
                {
                    value = "";
                }
                else if (len <= NetContext.BufferSize)
                {
                    byte[] buffer = null;
                    try
                    {
                        buffer = context.GetBuffer();
                        NetContext.Fill(incoming, buffer, len);
                        value = Encoding.UTF8.GetString(buffer, 0, len);
                    } finally
                    {
                        context.Recycle(buffer);
                    }
                }
                else
                {
                    var buffer = new byte[len];
                    NetContext.Fill(incoming, buffer, len);
                    value = Encoding.UTF8.GetString(buffer, 0, len);
                }
                context.Handler.OnReceived(connection, value);
                return(len + 2);

            case 0xFF:
                // shutdown is 0xFF 0x00
                if (incoming.ReadByte() == 0x00)
                {
                    GracefulShutdown(context, connection);
                    return(2);
                }
                else
                {
                    throw new InvalidOperationException("protocol fail");
                }

            default:
                throw new InvalidOperationException("protocol fail");
            }
        }