WebSocketFrameReader.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. #if !BESTHTTP_DISABLE_WEBSOCKET && (!UNITY_WEBGL || UNITY_EDITOR)
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using BestHTTP.Extensions;
  6. using BestHTTP.PlatformSupport.Memory;
  7. namespace BestHTTP.WebSocket.Frames
  8. {
  9. /// <summary>
  10. /// Represents an incoming WebSocket Frame.
  11. /// </summary>
  12. public struct WebSocketFrameReader
  13. {
  14. #region Properties
  15. public byte Header { get; private set; }
  16. /// <summary>
  17. /// True if it's a final Frame in a sequence, or the only one.
  18. /// </summary>
  19. public bool IsFinal { get; private set; }
  20. /// <summary>
  21. /// The type of the Frame.
  22. /// </summary>
  23. public WebSocketFrameTypes Type { get; private set; }
  24. /// <summary>
  25. /// The decoded array of bytes.
  26. /// </summary>
  27. public BufferSegment Data { get; private set; }
  28. /// <summary>
  29. /// Textual representation of the received Data.
  30. /// </summary>
  31. public string DataAsText { get; private set; }
  32. #endregion
  33. #region Internal & Private Functions
  34. internal unsafe void Read(Stream stream)
  35. {
  36. // For the complete documentation for this section see:
  37. // http://tools.ietf.org/html/rfc6455#section-5.2
  38. this.Header = ReadByte(stream);
  39. // The first byte is the Final Bit and the type of the frame
  40. IsFinal = (this.Header & 0x80) != 0;
  41. Type = (WebSocketFrameTypes)(this.Header & 0xF);
  42. byte maskAndLength = ReadByte(stream);
  43. // The second byte is the Mask Bit and the length of the payload data
  44. if ((maskAndLength & 0x80) != 0)
  45. throw new NotImplementedException($"Payload from the server is masked!");
  46. // if 0-125, that is the payload length.
  47. var length = (UInt64)(maskAndLength & 127);
  48. // If 126, the following 2 bytes interpreted as a 16-bit unsigned integer are the payload length.
  49. if (length == 126)
  50. {
  51. byte[] rawLen = BufferPool.Get(2, true);
  52. stream.ReadBuffer(rawLen, 2);
  53. if (BitConverter.IsLittleEndian)
  54. Array.Reverse(rawLen, 0, 2);
  55. length = (UInt64)BitConverter.ToUInt16(rawLen, 0);
  56. BufferPool.Release(rawLen);
  57. }
  58. else if (length == 127)
  59. {
  60. // If 127, the following 8 bytes interpreted as a 64-bit unsigned integer (the
  61. // most significant bit MUST be 0) are the payload length.
  62. byte[] rawLen = BufferPool.Get(8, true);
  63. stream.ReadBuffer(rawLen, 8);
  64. if (BitConverter.IsLittleEndian)
  65. Array.Reverse(rawLen, 0, 8);
  66. length = (UInt64)BitConverter.ToUInt64(rawLen, 0);
  67. BufferPool.Release(rawLen);
  68. }
  69. if (length == 0L)
  70. {
  71. Data = BufferSegment.Empty;
  72. return;
  73. }
  74. var buffer = BufferPool.Get((long)length, true);
  75. uint readLength = 0;
  76. try
  77. {
  78. do
  79. {
  80. int read = stream.Read(buffer, (int)readLength, (int)(length - readLength));
  81. if (read <= 0)
  82. throw ExceptionHelper.ServerClosedTCPStream();
  83. readLength += (uint)read;
  84. } while (readLength < length);
  85. }
  86. catch
  87. {
  88. BufferPool.Release(buffer);
  89. throw;
  90. }
  91. this.Data = new BufferSegment(buffer, 0, (int)length);
  92. }
  93. private byte ReadByte(Stream stream)
  94. {
  95. int read = stream.ReadByte();
  96. if (read < 0)
  97. throw ExceptionHelper.ServerClosedTCPStream();
  98. return (byte)read;
  99. }
  100. #endregion
  101. #region Public Functions
  102. /// <summary>
  103. /// Assembles all fragments into a final frame. Call this on the last fragment of a frame.
  104. /// </summary>
  105. /// <param name="fragments">The list of previously downloaded and parsed fragments of the frame</param>
  106. public void Assemble(List<WebSocketFrameReader> fragments)
  107. {
  108. // this way the following algorithms will handle this fragment's data too
  109. fragments.Add(this);
  110. UInt64 finalLength = 0;
  111. for (int i = 0; i < fragments.Count; ++i)
  112. finalLength += (UInt64)fragments[i].Data.Count;
  113. byte[] buffer = BufferPool.Get((long)finalLength, true);
  114. UInt64 pos = 0;
  115. for (int i = 0; i < fragments.Count; ++i)
  116. {
  117. if (fragments[i].Data.Count > 0)
  118. Array.Copy(fragments[i].Data.Data, fragments[i].Data.Offset, buffer, (int)pos, (int)fragments[i].Data.Count);
  119. fragments[i].ReleaseData();
  120. pos += (UInt64)fragments[i].Data.Count;
  121. }
  122. // All fragments of a message are of the same type, as set by the first fragment's opcode.
  123. this.Type = fragments[0].Type;
  124. // Reserver flags may be contained only in the first fragment
  125. this.Header = fragments[0].Header;
  126. this.Data = new BufferSegment(buffer, 0, (int)finalLength);
  127. }
  128. /// <summary>
  129. /// This function will decode the received data incrementally with the associated websocket's extensions.
  130. /// </summary>
  131. public void DecodeWithExtensions(WebSocket webSocket)
  132. {
  133. if (webSocket.Extensions != null)
  134. for (int i = 0; i < webSocket.Extensions.Length; ++i)
  135. {
  136. var ext = webSocket.Extensions[i];
  137. if (ext != null)
  138. {
  139. var newData = ext.Decode(this.Header, this.Data);
  140. if (this.Data != newData)
  141. {
  142. this.ReleaseData();
  143. this.Data = newData;
  144. }
  145. }
  146. }
  147. if (this.Type == WebSocketFrameTypes.Text)
  148. {
  149. if (this.Data != BufferSegment.Empty)
  150. {
  151. this.DataAsText = System.Text.Encoding.UTF8.GetString(this.Data.Data, this.Data.Offset, this.Data.Count);
  152. this.ReleaseData();
  153. }
  154. else
  155. HTTPManager.Logger.Warning("WebSocketFrameReader", "Empty Text frame received!");
  156. }
  157. }
  158. public void ReleaseData()
  159. {
  160. BufferPool.Release(this.Data);
  161. this.Data = BufferSegment.Empty;
  162. }
  163. public override string ToString()
  164. {
  165. return string.Format("[{0} Header: {1:X2}, IsFinal: {2}, Data: {3}]", this.Type.ToString(), this.Header, this.IsFinal, this.Data);
  166. }
  167. #endregion
  168. }
  169. }
  170. #endif