WebSocketFrame.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  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
  87. {
  88. get
  89. {
  90. return _extPayloadLength;
  91. }
  92. }
  93. public Fin Fin
  94. {
  95. get
  96. {
  97. return _fin;
  98. }
  99. }
  100. public bool IsBinary
  101. {
  102. get
  103. {
  104. return _opcode == Opcode.Binary;
  105. }
  106. }
  107. public bool IsClose
  108. {
  109. get
  110. {
  111. return _opcode == Opcode.Close;
  112. }
  113. }
  114. public bool IsCompressed
  115. {
  116. get
  117. {
  118. return _rsv1 == Rsv.On;
  119. }
  120. }
  121. public bool IsContinuation
  122. {
  123. get
  124. {
  125. return _opcode == Opcode.Cont;
  126. }
  127. }
  128. public bool IsControl
  129. {
  130. get
  131. {
  132. return _opcode == Opcode.Close || _opcode == Opcode.Ping || _opcode == Opcode.Pong;
  133. }
  134. }
  135. public bool IsData
  136. {
  137. get
  138. {
  139. return _opcode == Opcode.Binary || _opcode == Opcode.Text;
  140. }
  141. }
  142. public bool IsFinal
  143. {
  144. get
  145. {
  146. return _fin == Fin.Final;
  147. }
  148. }
  149. public bool IsFragmented
  150. {
  151. get
  152. {
  153. return _fin == Fin.More || _opcode == Opcode.Cont;
  154. }
  155. }
  156. public bool IsMasked
  157. {
  158. get
  159. {
  160. return _mask == Mask.Mask;
  161. }
  162. }
  163. public bool IsPerMessageCompressed
  164. {
  165. get
  166. {
  167. return (_opcode == Opcode.Binary || _opcode == Opcode.Text) && _rsv1 == Rsv.On;
  168. }
  169. }
  170. public bool IsPing
  171. {
  172. get
  173. {
  174. return _opcode == Opcode.Ping;
  175. }
  176. }
  177. public bool IsPong
  178. {
  179. get
  180. {
  181. return _opcode == Opcode.Pong;
  182. }
  183. }
  184. public bool IsText
  185. {
  186. get
  187. {
  188. return _opcode == Opcode.Text;
  189. }
  190. }
  191. public ulong Length
  192. {
  193. get
  194. {
  195. return 2 + (ulong)(_extPayloadLength.Length + _maskingKey.Length) + _payloadData.Length;
  196. }
  197. }
  198. public Mask Mask
  199. {
  200. get
  201. {
  202. return _mask;
  203. }
  204. }
  205. public byte[] MaskingKey
  206. {
  207. get
  208. {
  209. return _maskingKey;
  210. }
  211. }
  212. public Opcode Opcode
  213. {
  214. get
  215. {
  216. return _opcode;
  217. }
  218. }
  219. public PayloadData PayloadData
  220. {
  221. get
  222. {
  223. return _payloadData;
  224. }
  225. }
  226. public byte PayloadLength
  227. {
  228. get
  229. {
  230. return _payloadLength;
  231. }
  232. }
  233. public Rsv Rsv1
  234. {
  235. get
  236. {
  237. return _rsv1;
  238. }
  239. }
  240. public Rsv Rsv2
  241. {
  242. get
  243. {
  244. return _rsv2;
  245. }
  246. }
  247. public Rsv Rsv3
  248. {
  249. get
  250. {
  251. return _rsv3;
  252. }
  253. }
  254. #endregion
  255. #region Private Methods
  256. private byte[] createMaskingKey()
  257. {
  258. var key = new byte[4];
  259. var rand = new Random();
  260. rand.NextBytes(key);
  261. return key;
  262. }
  263. private static bool isControl(Opcode opcode)
  264. {
  265. return opcode == Opcode.Close || opcode == Opcode.Ping || opcode == Opcode.Pong;
  266. }
  267. private static bool isData(Opcode opcode)
  268. {
  269. return opcode == Opcode.Text || opcode == Opcode.Binary;
  270. }
  271. private static WebSocketFrame read(byte[] header, Stream stream, bool unmask)
  272. {
  273. /* Header */
  274. // FIN
  275. var fin = (header[0] & 0x80) == 0x80 ? Fin.Final : Fin.More;
  276. // RSV1
  277. var rsv1 = (header[0] & 0x40) == 0x40 ? Rsv.On : Rsv.Off;
  278. // RSV2
  279. var rsv2 = (header[0] & 0x20) == 0x20 ? Rsv.On : Rsv.Off;
  280. // RSV3
  281. var rsv3 = (header[0] & 0x10) == 0x10 ? Rsv.On : Rsv.Off;
  282. // Opcode
  283. var opcode = (Opcode)(header[0] & 0x0f);
  284. // MASK
  285. var mask = (header[1] & 0x80) == 0x80 ? Mask.Mask : Mask.Unmask;
  286. // Payload Length
  287. var payloadLen = (byte)(header[1] & 0x7f);
  288. // Check if correct frame.
  289. var incorrect = isControl(opcode) && fin == Fin.More
  290. ? "A control frame is fragmented."
  291. : !isData(opcode) && rsv1 == Rsv.On
  292. ? "A non data frame is compressed."
  293. : null;
  294. if (incorrect != null)
  295. throw new WebSocketException(CloseStatusCode.IncorrectData, incorrect);
  296. // Check if consistent frame.
  297. if (isControl(opcode) && payloadLen > 125)
  298. throw new WebSocketException(
  299. CloseStatusCode.InconsistentData,
  300. "The length of payload data of a control frame is greater than 125 bytes.");
  301. var frame = new WebSocketFrame();
  302. frame._fin = fin;
  303. frame._rsv1 = rsv1;
  304. frame._rsv2 = rsv2;
  305. frame._rsv3 = rsv3;
  306. frame._opcode = opcode;
  307. frame._mask = mask;
  308. frame._payloadLength = payloadLen;
  309. /* Extended Payload Length */
  310. var size = payloadLen < 126
  311. ? 0
  312. : payloadLen == 126
  313. ? 2
  314. : 8;
  315. var extPayloadLen = size > 0 ? stream.ReadBytes(size) : new byte[0];
  316. if (size > 0 && extPayloadLen.Length != size)
  317. throw new WebSocketException(
  318. "The 'Extended Payload Length' of a frame cannot be read from the data source.");
  319. frame._extPayloadLength = extPayloadLen;
  320. /* Masking Key */
  321. var masked = mask == Mask.Mask;
  322. var maskingKey = masked ? stream.ReadBytes(4) : new byte[0];
  323. if (masked && maskingKey.Length != 4)
  324. throw new WebSocketException(
  325. "The 'Masking Key' of a frame cannot be read from the data source.");
  326. frame._maskingKey = maskingKey;
  327. /* Payload Data */
  328. ulong len = payloadLen < 126
  329. ? payloadLen
  330. : payloadLen == 126
  331. ? extPayloadLen.ToUInt16(ByteOrder.Big)
  332. : extPayloadLen.ToUInt64(ByteOrder.Big);
  333. byte[] data = null;
  334. if (len > 0)
  335. {
  336. // Check if allowable payload data length.
  337. if (payloadLen > 126 && len > PayloadData.MaxLength)
  338. throw new WebSocketException(
  339. CloseStatusCode.TooBig,
  340. "The length of 'Payload Data' of a frame is greater than the allowable length.");
  341. data = payloadLen > 126
  342. ? stream.ReadBytes((long)len, 1024)
  343. : stream.ReadBytes((int)len);
  344. //if (data.LongLength != (long)len)
  345. // throw new WebSocketException(
  346. // "The 'Payload Data' of a frame cannot be read from the data source.");
  347. }
  348. else
  349. {
  350. data = new byte[0];
  351. }
  352. var payload = new PayloadData(data, masked);
  353. if (masked && unmask)
  354. {
  355. payload.Mask(maskingKey);
  356. frame._mask = Mask.Unmask;
  357. frame._maskingKey = new byte[0];
  358. }
  359. frame._payloadData = payload;
  360. return frame;
  361. }
  362. #endregion
  363. #region Internal Methods
  364. internal static WebSocketFrame CreateCloseFrame(Mask mask, byte[] data)
  365. {
  366. return new WebSocketFrame(Opcode.Close, mask, new PayloadData(data));
  367. }
  368. internal static WebSocketFrame CreateCloseFrame(Mask mask, PayloadData payload)
  369. {
  370. return new WebSocketFrame(Opcode.Close, mask, payload);
  371. }
  372. internal static WebSocketFrame CreateCloseFrame(Mask mask, CloseStatusCode code, string reason)
  373. {
  374. return new WebSocketFrame(
  375. Opcode.Close, mask, new PayloadData(((ushort)code).Append(reason)));
  376. }
  377. internal static WebSocketFrame CreatePingFrame(Mask mask)
  378. {
  379. return new WebSocketFrame(Opcode.Ping, mask, new PayloadData());
  380. }
  381. internal static WebSocketFrame CreatePingFrame(Mask mask, byte[] data)
  382. {
  383. return new WebSocketFrame(Opcode.Ping, mask, new PayloadData(data));
  384. }
  385. internal static WebSocketFrame CreatePongFrame(Mask mask, PayloadData payload)
  386. {
  387. return new WebSocketFrame(Opcode.Pong, mask, payload);
  388. }
  389. internal static WebSocketFrame CreateWebSocketFrame(
  390. Fin fin, Opcode opcode, Mask mask, byte[] data, bool compressed)
  391. {
  392. return new WebSocketFrame(fin, opcode, mask, new PayloadData(data), compressed);
  393. }
  394. internal static WebSocketFrame Read(Stream stream)
  395. {
  396. return Read(stream, true);
  397. }
  398. internal static WebSocketFrame Read(Stream stream, bool unmask)
  399. {
  400. var header = stream.ReadBytes(2);
  401. if (header.Length != 2)
  402. throw new WebSocketException(
  403. "The header part of a frame cannot be read from the data source.");
  404. return read(header, stream, unmask);
  405. }
  406. internal static async void ReadAsync(
  407. Stream stream, bool unmask, Action<WebSocketFrame> completed, Action<Exception> error)
  408. {
  409. try
  410. {
  411. var header = await stream.ReadBytesAsync(2).ConfigureAwait(false);
  412. if (header.Length != 2)
  413. throw new WebSocketException(
  414. "The header part of a frame cannot be read from the data source.");
  415. var frame = read(header, stream, unmask);
  416. if (completed != null)
  417. completed(frame);
  418. }
  419. catch (Exception ex)
  420. {
  421. if (error != null)
  422. {
  423. error(ex);
  424. }
  425. }
  426. }
  427. #endregion
  428. #region Public Methods
  429. public IEnumerator<byte> GetEnumerator()
  430. {
  431. foreach (var b in ToByteArray())
  432. yield return b;
  433. }
  434. public void Print(bool dumped)
  435. {
  436. //Console.WriteLine(dumped ? dump(this) : print(this));
  437. }
  438. public byte[] ToByteArray()
  439. {
  440. using (var buff = new MemoryStream())
  441. {
  442. var header = (int)_fin;
  443. header = (header << 1) + (int)_rsv1;
  444. header = (header << 1) + (int)_rsv2;
  445. header = (header << 1) + (int)_rsv3;
  446. header = (header << 4) + (int)_opcode;
  447. header = (header << 1) + (int)_mask;
  448. header = (header << 7) + (int)_payloadLength;
  449. buff.Write(((ushort)header).ToByteArrayInternally(ByteOrder.Big), 0, 2);
  450. if (_payloadLength > 125)
  451. buff.Write(_extPayloadLength, 0, _extPayloadLength.Length);
  452. if (_mask == Mask.Mask)
  453. buff.Write(_maskingKey, 0, _maskingKey.Length);
  454. if (_payloadLength > 0)
  455. {
  456. var payload = _payloadData.ToByteArray();
  457. if (_payloadLength < 127)
  458. buff.Write(payload, 0, payload.Length);
  459. else
  460. buff.WriteBytes(payload);
  461. }
  462. return buff.ToArray();
  463. }
  464. }
  465. public override string ToString()
  466. {
  467. return BitConverter.ToString(ToByteArray());
  468. }
  469. #endregion
  470. #region Explicitly Implemented Interface Members
  471. IEnumerator IEnumerable.GetEnumerator()
  472. {
  473. return GetEnumerator();
  474. }
  475. #endregion
  476. }
  477. }