| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578 | using System;using System.Collections;using System.Collections.Generic;using System.IO;using System.Text;namespace SocketHttpListener{    internal class WebSocketFrame : IEnumerable<byte>    {        #region Private Fields        private byte[] _extPayloadLength;        private Fin _fin;        private Mask _mask;        private byte[] _maskingKey;        private Opcode _opcode;        private PayloadData _payloadData;        private byte _payloadLength;        private Rsv _rsv1;        private Rsv _rsv2;        private Rsv _rsv3;        #endregion        #region Internal Fields        internal static readonly byte[] EmptyUnmaskPingData;        #endregion        #region Static Constructor        static WebSocketFrame()        {            EmptyUnmaskPingData = CreatePingFrame(Mask.Unmask).ToByteArray();        }        #endregion        #region Private Constructors        private WebSocketFrame()        {        }        #endregion        #region Internal Constructors        internal WebSocketFrame(Opcode opcode, PayloadData payload)            : this(Fin.Final, opcode, Mask.Mask, payload, false)        {        }        internal WebSocketFrame(Opcode opcode, Mask mask, PayloadData payload)            : this(Fin.Final, opcode, mask, payload, false)        {        }        internal WebSocketFrame(Fin fin, Opcode opcode, Mask mask, PayloadData payload)            : this(fin, opcode, mask, payload, false)        {        }        internal WebSocketFrame(          Fin fin, Opcode opcode, Mask mask, PayloadData payload, bool compressed)        {            _fin = fin;            _rsv1 = isData(opcode) && compressed ? Rsv.On : Rsv.Off;            _rsv2 = Rsv.Off;            _rsv3 = Rsv.Off;            _opcode = opcode;            _mask = mask;            var len = payload.Length;            if (len < 126)            {                _payloadLength = (byte)len;                _extPayloadLength = new byte[0];            }            else if (len < 0x010000)            {                _payloadLength = (byte)126;                _extPayloadLength = ((ushort)len).ToByteArrayInternally(ByteOrder.Big);            }            else            {                _payloadLength = (byte)127;                _extPayloadLength = len.ToByteArrayInternally(ByteOrder.Big);            }            if (mask == Mask.Mask)            {                _maskingKey = createMaskingKey();                payload.Mask(_maskingKey);            }            else            {                _maskingKey = new byte[0];            }            _payloadData = payload;        }        #endregion        #region Public Properties        public byte[] ExtendedPayloadLength        {            get            {                return _extPayloadLength;            }        }        public Fin Fin        {            get            {                return _fin;            }        }        public bool IsBinary        {            get            {                return _opcode == Opcode.Binary;            }        }        public bool IsClose        {            get            {                return _opcode == Opcode.Close;            }        }        public bool IsCompressed        {            get            {                return _rsv1 == Rsv.On;            }        }        public bool IsContinuation        {            get            {                return _opcode == Opcode.Cont;            }        }        public bool IsControl        {            get            {                return _opcode == Opcode.Close || _opcode == Opcode.Ping || _opcode == Opcode.Pong;            }        }        public bool IsData        {            get            {                return _opcode == Opcode.Binary || _opcode == Opcode.Text;            }        }        public bool IsFinal        {            get            {                return _fin == Fin.Final;            }        }        public bool IsFragmented        {            get            {                return _fin == Fin.More || _opcode == Opcode.Cont;            }        }        public bool IsMasked        {            get            {                return _mask == Mask.Mask;            }        }        public bool IsPerMessageCompressed        {            get            {                return (_opcode == Opcode.Binary || _opcode == Opcode.Text) && _rsv1 == Rsv.On;            }        }        public bool IsPing        {            get            {                return _opcode == Opcode.Ping;            }        }        public bool IsPong        {            get            {                return _opcode == Opcode.Pong;            }        }        public bool IsText        {            get            {                return _opcode == Opcode.Text;            }        }        public ulong Length        {            get            {                return 2 + (ulong)(_extPayloadLength.Length + _maskingKey.Length) + _payloadData.Length;            }        }        public Mask Mask        {            get            {                return _mask;            }        }        public byte[] MaskingKey        {            get            {                return _maskingKey;            }        }        public Opcode Opcode        {            get            {                return _opcode;            }        }        public PayloadData PayloadData        {            get            {                return _payloadData;            }        }        public byte PayloadLength        {            get            {                return _payloadLength;            }        }        public Rsv Rsv1        {            get            {                return _rsv1;            }        }        public Rsv Rsv2        {            get            {                return _rsv2;            }        }        public Rsv Rsv3        {            get            {                return _rsv3;            }        }        #endregion        #region Private Methods        private byte[] createMaskingKey()        {            var key = new byte[4];            var rand = new Random();            rand.NextBytes(key);            return key;        }        private static bool isControl(Opcode opcode)        {            return opcode == Opcode.Close || opcode == Opcode.Ping || opcode == Opcode.Pong;        }        private static bool isData(Opcode opcode)        {            return opcode == Opcode.Text || opcode == Opcode.Binary;        }        private static WebSocketFrame read(byte[] header, Stream stream, bool unmask)        {            /* Header */            // FIN            var fin = (header[0] & 0x80) == 0x80 ? Fin.Final : Fin.More;            // RSV1            var rsv1 = (header[0] & 0x40) == 0x40 ? Rsv.On : Rsv.Off;            // RSV2            var rsv2 = (header[0] & 0x20) == 0x20 ? Rsv.On : Rsv.Off;            // RSV3            var rsv3 = (header[0] & 0x10) == 0x10 ? Rsv.On : Rsv.Off;            // Opcode            var opcode = (Opcode)(header[0] & 0x0f);            // MASK            var mask = (header[1] & 0x80) == 0x80 ? Mask.Mask : Mask.Unmask;            // Payload Length            var payloadLen = (byte)(header[1] & 0x7f);            // Check if correct frame.            var incorrect = isControl(opcode) && fin == Fin.More                            ? "A control frame is fragmented."                            : !isData(opcode) && rsv1 == Rsv.On                              ? "A non data frame is compressed."                              : null;            if (incorrect != null)                throw new WebSocketException(CloseStatusCode.IncorrectData, incorrect);            // Check if consistent frame.            if (isControl(opcode) && payloadLen > 125)                throw new WebSocketException(                  CloseStatusCode.InconsistentData,                  "The length of payload data of a control frame is greater than 125 bytes.");            var frame = new WebSocketFrame();            frame._fin = fin;            frame._rsv1 = rsv1;            frame._rsv2 = rsv2;            frame._rsv3 = rsv3;            frame._opcode = opcode;            frame._mask = mask;            frame._payloadLength = payloadLen;            /* Extended Payload Length */            var size = payloadLen < 126                       ? 0                       : payloadLen == 126                         ? 2                         : 8;            var extPayloadLen = size > 0 ? stream.ReadBytes(size) : new byte[0];            if (size > 0 && extPayloadLen.Length != size)                throw new WebSocketException(                  "The 'Extended Payload Length' of a frame cannot be read from the data source.");            frame._extPayloadLength = extPayloadLen;            /* Masking Key */            var masked = mask == Mask.Mask;            var maskingKey = masked ? stream.ReadBytes(4) : new byte[0];            if (masked && maskingKey.Length != 4)                throw new WebSocketException(                  "The 'Masking Key' of a frame cannot be read from the data source.");            frame._maskingKey = maskingKey;            /* Payload Data */            ulong len = payloadLen < 126                        ? payloadLen                        : payloadLen == 126                          ? extPayloadLen.ToUInt16(ByteOrder.Big)                          : extPayloadLen.ToUInt64(ByteOrder.Big);            byte[] data = null;            if (len > 0)            {                // Check if allowable payload data length.                if (payloadLen > 126 && len > PayloadData.MaxLength)                    throw new WebSocketException(                      CloseStatusCode.TooBig,                      "The length of 'Payload Data' of a frame is greater than the allowable length.");                data = payloadLen > 126                       ? stream.ReadBytes((long)len, 1024)                       : stream.ReadBytes((int)len);                //if (data.LongLength != (long)len)                //    throw new WebSocketException(                //      "The 'Payload Data' of a frame cannot be read from the data source.");            }            else            {                data = new byte[0];            }            var payload = new PayloadData(data, masked);            if (masked && unmask)            {                payload.Mask(maskingKey);                frame._mask = Mask.Unmask;                frame._maskingKey = new byte[0];            }            frame._payloadData = payload;            return frame;        }        #endregion        #region Internal Methods        internal static WebSocketFrame CreateCloseFrame(Mask mask, byte[] data)        {            return new WebSocketFrame(Opcode.Close, mask, new PayloadData(data));        }        internal static WebSocketFrame CreateCloseFrame(Mask mask, PayloadData payload)        {            return new WebSocketFrame(Opcode.Close, mask, payload);        }        internal static WebSocketFrame CreateCloseFrame(Mask mask, CloseStatusCode code, string reason)        {            return new WebSocketFrame(              Opcode.Close, mask, new PayloadData(((ushort)code).Append(reason)));        }        internal static WebSocketFrame CreatePingFrame(Mask mask)        {            return new WebSocketFrame(Opcode.Ping, mask, new PayloadData());        }        internal static WebSocketFrame CreatePingFrame(Mask mask, byte[] data)        {            return new WebSocketFrame(Opcode.Ping, mask, new PayloadData(data));        }        internal static WebSocketFrame CreatePongFrame(Mask mask, PayloadData payload)        {            return new WebSocketFrame(Opcode.Pong, mask, payload);        }        internal static WebSocketFrame CreateWebSocketFrame(          Fin fin, Opcode opcode, Mask mask, byte[] data, bool compressed)        {            return new WebSocketFrame(fin, opcode, mask, new PayloadData(data), compressed);        }        internal static WebSocketFrame Read(Stream stream)        {            return Read(stream, true);        }        internal static WebSocketFrame Read(Stream stream, bool unmask)        {            var header = stream.ReadBytes(2);            if (header.Length != 2)                throw new WebSocketException(                  "The header part of a frame cannot be read from the data source.");            return read(header, stream, unmask);        }        internal static async void ReadAsync(          Stream stream, bool unmask, Action<WebSocketFrame> completed, Action<Exception> error)        {            try            {                var header = await stream.ReadBytesAsync(2).ConfigureAwait(false);                if (header.Length != 2)                    throw new WebSocketException(                      "The header part of a frame cannot be read from the data source.");                var frame = read(header, stream, unmask);                if (completed != null)                    completed(frame);            }            catch (Exception ex)            {                if (error != null)                {                    error(ex);                }            }        }        #endregion        #region Public Methods        public IEnumerator<byte> GetEnumerator()        {            foreach (var b in ToByteArray())                yield return b;        }        public void Print(bool dumped)        {            //Console.WriteLine(dumped ? dump(this) : print(this));        }        public byte[] ToByteArray()        {            using (var buff = new MemoryStream())            {                var header = (int)_fin;                header = (header << 1) + (int)_rsv1;                header = (header << 1) + (int)_rsv2;                header = (header << 1) + (int)_rsv3;                header = (header << 4) + (int)_opcode;                header = (header << 1) + (int)_mask;                header = (header << 7) + (int)_payloadLength;                buff.Write(((ushort)header).ToByteArrayInternally(ByteOrder.Big), 0, 2);                if (_payloadLength > 125)                    buff.Write(_extPayloadLength, 0, _extPayloadLength.Length);                if (_mask == Mask.Mask)                    buff.Write(_maskingKey, 0, _maskingKey.Length);                if (_payloadLength > 0)                {                    var payload = _payloadData.ToByteArray();                    if (_payloadLength < 127)                        buff.Write(payload, 0, payload.Length);                    else                        buff.WriteBytes(payload);                }                return buff.ToArray();            }        }        public override string ToString()        {            return BitConverter.ToString(ToByteArray());        }        #endregion        #region Explicitly Implemented Interface Members        IEnumerator IEnumerable.GetEnumerator()        {            return GetEnumerator();        }        #endregion    }}
 |