WebSocketFrame.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. namespace SocketHttpListener
  6. {
  7. internal class WebSocketFrame : IEnumerable<byte>
  8. {
  9. #region Private Fields
  10. private byte[] _extPayloadLength;
  11. private Fin _fin;
  12. private Mask _mask;
  13. private byte[] _maskingKey;
  14. private Opcode _opcode;
  15. private PayloadData _payloadData;
  16. private byte _payloadLength;
  17. private Rsv _rsv1;
  18. private Rsv _rsv2;
  19. private Rsv _rsv3;
  20. #endregion
  21. #region Internal Fields
  22. internal static readonly byte[] EmptyUnmaskPingData;
  23. #endregion
  24. #region Static Constructor
  25. static WebSocketFrame()
  26. {
  27. EmptyUnmaskPingData = CreatePingFrame(Mask.Unmask).ToByteArray();
  28. }
  29. #endregion
  30. #region Private Constructors
  31. private WebSocketFrame()
  32. {
  33. }
  34. #endregion
  35. #region Internal Constructors
  36. internal WebSocketFrame(Opcode opcode, PayloadData payload)
  37. : this(Fin.Final, opcode, Mask.Mask, payload, false)
  38. {
  39. }
  40. internal WebSocketFrame(Opcode opcode, Mask mask, PayloadData payload)
  41. : this(Fin.Final, opcode, mask, payload, false)
  42. {
  43. }
  44. internal WebSocketFrame(Fin fin, Opcode opcode, Mask mask, PayloadData payload)
  45. : this(fin, opcode, mask, payload, false)
  46. {
  47. }
  48. internal WebSocketFrame(
  49. Fin fin, Opcode opcode, Mask mask, PayloadData payload, bool compressed)
  50. {
  51. _fin = fin;
  52. _rsv1 = isData(opcode) && compressed ? Rsv.On : Rsv.Off;
  53. _rsv2 = Rsv.Off;
  54. _rsv3 = Rsv.Off;
  55. _opcode = opcode;
  56. _mask = mask;
  57. var len = payload.Length;
  58. if (len < 126)
  59. {
  60. _payloadLength = (byte)len;
  61. _extPayloadLength = new byte[0];
  62. }
  63. else if (len < 0x010000)
  64. {
  65. _payloadLength = (byte)126;
  66. _extPayloadLength = ((ushort)len).ToByteArrayInternally(ByteOrder.Big);
  67. }
  68. else
  69. {
  70. _payloadLength = (byte)127;
  71. _extPayloadLength = len.ToByteArrayInternally(ByteOrder.Big);
  72. }
  73. if (mask == Mask.Mask)
  74. {
  75. _maskingKey = createMaskingKey();
  76. payload.Mask(_maskingKey);
  77. }
  78. else
  79. {
  80. _maskingKey = new byte[0];
  81. }
  82. _payloadData = payload;
  83. }
  84. #endregion
  85. #region Public Properties
  86. public byte[] ExtendedPayloadLength => _extPayloadLength;
  87. public Fin Fin => _fin;
  88. public bool IsBinary => _opcode == Opcode.Binary;
  89. public bool IsClose => _opcode == Opcode.Close;
  90. public bool IsCompressed => _rsv1 == Rsv.On;
  91. public bool IsContinuation => _opcode == Opcode.Cont;
  92. public bool IsControl => _opcode == Opcode.Close || _opcode == Opcode.Ping || _opcode == Opcode.Pong;
  93. public bool IsData => _opcode == Opcode.Binary || _opcode == Opcode.Text;
  94. public bool IsFinal => _fin == Fin.Final;
  95. public bool IsFragmented => _fin == Fin.More || _opcode == Opcode.Cont;
  96. public bool IsMasked => _mask == Mask.Mask;
  97. public bool IsPerMessageCompressed => (_opcode == Opcode.Binary || _opcode == Opcode.Text) && _rsv1 == Rsv.On;
  98. public bool IsPing => _opcode == Opcode.Ping;
  99. public bool IsPong => _opcode == Opcode.Pong;
  100. public bool IsText => _opcode == Opcode.Text;
  101. public ulong Length => 2 + (ulong)(_extPayloadLength.Length + _maskingKey.Length) + _payloadData.Length;
  102. public Mask Mask => _mask;
  103. public byte[] MaskingKey => _maskingKey;
  104. public Opcode Opcode => _opcode;
  105. public PayloadData PayloadData => _payloadData;
  106. public byte PayloadLength => _payloadLength;
  107. public Rsv Rsv1 => _rsv1;
  108. public Rsv Rsv2 => _rsv2;
  109. public Rsv Rsv3 => _rsv3;
  110. #endregion
  111. #region Private Methods
  112. private byte[] createMaskingKey()
  113. {
  114. var key = new byte[4];
  115. var rand = new Random();
  116. rand.NextBytes(key);
  117. return key;
  118. }
  119. private static bool isControl(Opcode opcode)
  120. {
  121. return opcode == Opcode.Close || opcode == Opcode.Ping || opcode == Opcode.Pong;
  122. }
  123. private static bool isData(Opcode opcode)
  124. {
  125. return opcode == Opcode.Text || opcode == Opcode.Binary;
  126. }
  127. private static WebSocketFrame read(byte[] header, Stream stream, bool unmask)
  128. {
  129. /* Header */
  130. // FIN
  131. var fin = (header[0] & 0x80) == 0x80 ? Fin.Final : Fin.More;
  132. // RSV1
  133. var rsv1 = (header[0] & 0x40) == 0x40 ? Rsv.On : Rsv.Off;
  134. // RSV2
  135. var rsv2 = (header[0] & 0x20) == 0x20 ? Rsv.On : Rsv.Off;
  136. // RSV3
  137. var rsv3 = (header[0] & 0x10) == 0x10 ? Rsv.On : Rsv.Off;
  138. // Opcode
  139. var opcode = (Opcode)(header[0] & 0x0f);
  140. // MASK
  141. var mask = (header[1] & 0x80) == 0x80 ? Mask.Mask : Mask.Unmask;
  142. // Payload Length
  143. var payloadLen = (byte)(header[1] & 0x7f);
  144. // Check if correct frame.
  145. var incorrect = isControl(opcode) && fin == Fin.More
  146. ? "A control frame is fragmented."
  147. : !isData(opcode) && rsv1 == Rsv.On
  148. ? "A non data frame is compressed."
  149. : null;
  150. if (incorrect != null)
  151. throw new WebSocketException(CloseStatusCode.IncorrectData, incorrect);
  152. // Check if consistent frame.
  153. if (isControl(opcode) && payloadLen > 125)
  154. throw new WebSocketException(
  155. CloseStatusCode.InconsistentData,
  156. "The length of payload data of a control frame is greater than 125 bytes.");
  157. var frame = new WebSocketFrame();
  158. frame._fin = fin;
  159. frame._rsv1 = rsv1;
  160. frame._rsv2 = rsv2;
  161. frame._rsv3 = rsv3;
  162. frame._opcode = opcode;
  163. frame._mask = mask;
  164. frame._payloadLength = payloadLen;
  165. /* Extended Payload Length */
  166. var size = payloadLen < 126
  167. ? 0
  168. : payloadLen == 126
  169. ? 2
  170. : 8;
  171. var extPayloadLen = size > 0 ? stream.ReadBytes(size) : new byte[0];
  172. if (size > 0 && extPayloadLen.Length != size)
  173. throw new WebSocketException(
  174. "The 'Extended Payload Length' of a frame cannot be read from the data source.");
  175. frame._extPayloadLength = extPayloadLen;
  176. /* Masking Key */
  177. var masked = mask == Mask.Mask;
  178. var maskingKey = masked ? stream.ReadBytes(4) : new byte[0];
  179. if (masked && maskingKey.Length != 4)
  180. throw new WebSocketException(
  181. "The 'Masking Key' of a frame cannot be read from the data source.");
  182. frame._maskingKey = maskingKey;
  183. /* Payload Data */
  184. ulong len = payloadLen < 126
  185. ? payloadLen
  186. : payloadLen == 126
  187. ? extPayloadLen.ToUInt16(ByteOrder.Big)
  188. : extPayloadLen.ToUInt64(ByteOrder.Big);
  189. byte[] data = null;
  190. if (len > 0)
  191. {
  192. // Check if allowable payload data length.
  193. if (payloadLen > 126 && len > PayloadData.MaxLength)
  194. throw new WebSocketException(
  195. CloseStatusCode.TooBig,
  196. "The length of 'Payload Data' of a frame is greater than the allowable length.");
  197. data = payloadLen > 126
  198. ? stream.ReadBytes((long)len, 1024)
  199. : stream.ReadBytes((int)len);
  200. //if (data.LongLength != (long)len)
  201. // throw new WebSocketException(
  202. // "The 'Payload Data' of a frame cannot be read from the data source.");
  203. }
  204. else
  205. {
  206. data = new byte[0];
  207. }
  208. var payload = new PayloadData(data, masked);
  209. if (masked && unmask)
  210. {
  211. payload.Mask(maskingKey);
  212. frame._mask = Mask.Unmask;
  213. frame._maskingKey = new byte[0];
  214. }
  215. frame._payloadData = payload;
  216. return frame;
  217. }
  218. #endregion
  219. #region Internal Methods
  220. internal static WebSocketFrame CreateCloseFrame(Mask mask, byte[] data)
  221. {
  222. return new WebSocketFrame(Opcode.Close, mask, new PayloadData(data));
  223. }
  224. internal static WebSocketFrame CreateCloseFrame(Mask mask, PayloadData payload)
  225. {
  226. return new WebSocketFrame(Opcode.Close, mask, payload);
  227. }
  228. internal static WebSocketFrame CreateCloseFrame(Mask mask, CloseStatusCode code, string reason)
  229. {
  230. return new WebSocketFrame(
  231. Opcode.Close, mask, new PayloadData(((ushort)code).Append(reason)));
  232. }
  233. internal static WebSocketFrame CreatePingFrame(Mask mask)
  234. {
  235. return new WebSocketFrame(Opcode.Ping, mask, new PayloadData());
  236. }
  237. internal static WebSocketFrame CreatePingFrame(Mask mask, byte[] data)
  238. {
  239. return new WebSocketFrame(Opcode.Ping, mask, new PayloadData(data));
  240. }
  241. internal static WebSocketFrame CreatePongFrame(Mask mask, PayloadData payload)
  242. {
  243. return new WebSocketFrame(Opcode.Pong, mask, payload);
  244. }
  245. internal static WebSocketFrame CreateWebSocketFrame(
  246. Fin fin, Opcode opcode, Mask mask, byte[] data, bool compressed)
  247. {
  248. return new WebSocketFrame(fin, opcode, mask, new PayloadData(data), compressed);
  249. }
  250. internal static WebSocketFrame Read(Stream stream)
  251. {
  252. return Read(stream, true);
  253. }
  254. internal static WebSocketFrame Read(Stream stream, bool unmask)
  255. {
  256. var header = stream.ReadBytes(2);
  257. if (header.Length != 2)
  258. throw new WebSocketException(
  259. "The header part of a frame cannot be read from the data source.");
  260. return read(header, stream, unmask);
  261. }
  262. internal static async void ReadAsync(
  263. Stream stream, bool unmask, Action<WebSocketFrame> completed, Action<Exception> error)
  264. {
  265. try
  266. {
  267. var header = await stream.ReadBytesAsync(2).ConfigureAwait(false);
  268. if (header.Length != 2)
  269. throw new WebSocketException(
  270. "The header part of a frame cannot be read from the data source.");
  271. var frame = read(header, stream, unmask);
  272. if (completed != null)
  273. completed(frame);
  274. }
  275. catch (Exception ex)
  276. {
  277. if (error != null)
  278. {
  279. error(ex);
  280. }
  281. }
  282. }
  283. #endregion
  284. #region Public Methods
  285. public IEnumerator<byte> GetEnumerator()
  286. {
  287. foreach (var b in ToByteArray())
  288. yield return b;
  289. }
  290. public void Print(bool dumped)
  291. {
  292. //Console.WriteLine(dumped ? dump(this) : print(this));
  293. }
  294. public byte[] ToByteArray()
  295. {
  296. using (var buff = new MemoryStream())
  297. {
  298. var header = (int)_fin;
  299. header = (header << 1) + (int)_rsv1;
  300. header = (header << 1) + (int)_rsv2;
  301. header = (header << 1) + (int)_rsv3;
  302. header = (header << 4) + (int)_opcode;
  303. header = (header << 1) + (int)_mask;
  304. header = (header << 7) + (int)_payloadLength;
  305. buff.Write(((ushort)header).ToByteArrayInternally(ByteOrder.Big), 0, 2);
  306. if (_payloadLength > 125)
  307. buff.Write(_extPayloadLength, 0, _extPayloadLength.Length);
  308. if (_mask == Mask.Mask)
  309. buff.Write(_maskingKey, 0, _maskingKey.Length);
  310. if (_payloadLength > 0)
  311. {
  312. var payload = _payloadData.ToByteArray();
  313. if (_payloadLength < 127)
  314. buff.Write(payload, 0, payload.Length);
  315. else
  316. buff.WriteBytes(payload);
  317. }
  318. return buff.ToArray();
  319. }
  320. }
  321. public override string ToString()
  322. {
  323. return BitConverter.ToString(ToByteArray());
  324. }
  325. #endregion
  326. #region Explicitly Implemented Interface Members
  327. IEnumerator IEnumerable.GetEnumerator()
  328. {
  329. return GetEnumerator();
  330. }
  331. #endregion
  332. }
  333. }