123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.IO;
- 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 => _extPayloadLength;
- public Fin Fin => _fin;
- public bool IsBinary => _opcode == Opcode.Binary;
- public bool IsClose => _opcode == Opcode.Close;
- public bool IsCompressed => _rsv1 == Rsv.On;
- public bool IsContinuation => _opcode == Opcode.Cont;
- public bool IsControl => _opcode == Opcode.Close || _opcode == Opcode.Ping || _opcode == Opcode.Pong;
- public bool IsData => _opcode == Opcode.Binary || _opcode == Opcode.Text;
- public bool IsFinal => _fin == Fin.Final;
- public bool IsFragmented => _fin == Fin.More || _opcode == Opcode.Cont;
- public bool IsMasked => _mask == Mask.Mask;
- public bool IsPerMessageCompressed => (_opcode == Opcode.Binary || _opcode == Opcode.Text) && _rsv1 == Rsv.On;
- public bool IsPing => _opcode == Opcode.Ping;
- public bool IsPong => _opcode == Opcode.Pong;
- public bool IsText => _opcode == Opcode.Text;
- public ulong Length => 2 + (ulong)(_extPayloadLength.Length + _maskingKey.Length) + _payloadData.Length;
- public Mask Mask => _mask;
- public byte[] MaskingKey => _maskingKey;
- public Opcode Opcode => _opcode;
- public PayloadData PayloadData => _payloadData;
- public byte PayloadLength => _payloadLength;
- public Rsv Rsv1 => _rsv1;
- public Rsv Rsv2 => _rsv2;
- public Rsv Rsv3 => _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
- }
- }
|