WebSocketFrame.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. #if !BESTHTTP_DISABLE_WEBSOCKET && (!UNITY_WEBGL || UNITY_EDITOR)
  2. using System;
  3. using System.IO;
  4. namespace BestHTTP.WebSocket.Frames
  5. {
  6. /// <summary>
  7. /// Denotes a binary frame. The "Payload data" is arbitrary binary data whose interpretation is solely up to the application layer.
  8. /// This is the base class of all other frame writers, as all frame can be represented as a byte array.
  9. /// </summary>
  10. public sealed class WebSocketFrame
  11. {
  12. public static readonly byte[] NoData = new byte[0];
  13. public WebSocketFrameTypes Type { get; private set; }
  14. public bool IsFinal { get; private set; }
  15. public byte Header { get; private set; }
  16. public byte[] Data { get; private set; }
  17. public bool UseExtensions { get; private set; }
  18. #region Constructors
  19. public WebSocketFrame(WebSocket webSocket, WebSocketFrameTypes type, byte[] data)
  20. :this(webSocket, type, data, true)
  21. { }
  22. public WebSocketFrame(WebSocket webSocket, WebSocketFrameTypes type, byte[] data, bool useExtensions)
  23. : this(webSocket, type, data, 0, data != null ? (UInt64)data.Length : 0, true, useExtensions)
  24. {
  25. }
  26. public WebSocketFrame(WebSocket webSocket, WebSocketFrameTypes type, byte[] data, bool isFinal, bool useExtensions)
  27. : this(webSocket, type, data, 0, data != null ? (UInt64)data.Length : 0, isFinal, useExtensions)
  28. {
  29. }
  30. public WebSocketFrame(WebSocket webSocket, WebSocketFrameTypes type, byte[] data, UInt64 pos, UInt64 length, bool isFinal, bool useExtensions)
  31. {
  32. this.Type = type;
  33. this.IsFinal = isFinal;
  34. this.UseExtensions = useExtensions;
  35. if (data != null)
  36. {
  37. this.Data = new byte[length];
  38. Array.Copy(data, (int)pos, this.Data, 0, (int)length);
  39. }
  40. else
  41. data = NoData;
  42. // First byte: Final Bit + Rsv flags + OpCode
  43. byte finalBit = (byte)(IsFinal ? 0x80 : 0x0);
  44. this.Header = (byte)(finalBit | (byte)Type);
  45. if (this.UseExtensions && webSocket != null && webSocket.Extensions != null)
  46. {
  47. for (int i = 0; i < webSocket.Extensions.Length; ++i)
  48. {
  49. var ext = webSocket.Extensions[i];
  50. if (ext != null)
  51. {
  52. this.Header |= ext.GetFrameHeader(this, this.Header);
  53. this.Data = ext.Encode(this);
  54. }
  55. }
  56. }
  57. }
  58. #endregion
  59. #region Public Functions
  60. public byte[] Get()
  61. {
  62. if (Data == null)
  63. Data = NoData;
  64. using (var ms = new MemoryStream(this.Data.Length + 9))
  65. {
  66. // For the complete documentation for this section see:
  67. // http://tools.ietf.org/html/rfc6455#section-5.2
  68. // Write the header
  69. ms.WriteByte(this.Header);
  70. // The length of the "Payload data", in bytes: if 0-125, that is the payload length. If 126, the following 2 bytes interpreted as a
  71. // 16-bit unsigned integer are the payload length. If 127, the following 8 bytes interpreted as a 64-bit unsigned integer (the
  72. // most significant bit MUST be 0) are the payload length. Multibyte length quantities are expressed in network byte order.
  73. if (this.Data.Length < 126)
  74. ms.WriteByte((byte)(0x80 | (byte)this.Data.Length));
  75. else if (this.Data.Length < UInt16.MaxValue)
  76. {
  77. ms.WriteByte((byte)(0x80 | 126));
  78. byte[] len = BitConverter.GetBytes((UInt16)this.Data.Length);
  79. if (BitConverter.IsLittleEndian)
  80. Array.Reverse(len, 0, len.Length);
  81. ms.Write(len, 0, len.Length);
  82. }
  83. else
  84. {
  85. ms.WriteByte((byte)(0x80 | 127));
  86. byte[] len = BitConverter.GetBytes((UInt64)this.Data.Length);
  87. if (BitConverter.IsLittleEndian)
  88. Array.Reverse(len, 0, len.Length);
  89. ms.Write(len, 0, len.Length);
  90. }
  91. // All frames sent from the client to the server are masked by a 32-bit value that is contained within the frame. This field is
  92. // present if the mask bit is set to 1 and is absent if the mask bit is set to 0.
  93. // If the data is being sent by the client, the frame(s) MUST be masked.
  94. byte[] mask = BitConverter.GetBytes((Int32)this.GetHashCode());
  95. ms.Write(mask, 0, mask.Length);
  96. // Do the masking.
  97. for (int i = 0; i < this.Data.Length; ++i)
  98. ms.WriteByte((byte)(Data[i] ^ mask[i % 4]));
  99. return ms.ToArray();
  100. }
  101. }
  102. public WebSocketFrame[] Fragment(ushort maxFragmentSize)
  103. {
  104. if (this.Data == null)
  105. return null;
  106. // All control frames MUST have a payload length of 125 bytes or less and MUST NOT be fragmented.
  107. if (this.Type != WebSocketFrameTypes.Binary && this.Type != WebSocketFrameTypes.Text)
  108. return null;
  109. if (this.Data.Length <= maxFragmentSize)
  110. return null;
  111. this.IsFinal = false;
  112. // Clear final bit from the header flags
  113. this.Header &= 0x7F;
  114. // One chunk will remain in this fragment, so we have to allocate one less
  115. int count = (this.Data.Length / maxFragmentSize) + (this.Data.Length % maxFragmentSize == 0 ? -1 : 0);
  116. WebSocketFrame[] fragments = new WebSocketFrame[count];
  117. // Skip one chunk, for the current one
  118. UInt64 pos = maxFragmentSize;
  119. while (pos < (UInt64)this.Data.Length)
  120. {
  121. UInt64 chunkLength = Math.Min(maxFragmentSize, (UInt64)this.Data.Length - pos);
  122. fragments[fragments.Length - count--] = new WebSocketFrame(null, WebSocketFrameTypes.Continuation, this.Data, pos, chunkLength, pos + chunkLength >= (UInt64)this.Data.Length, false);
  123. pos += chunkLength;
  124. }
  125. byte[] newData = new byte[maxFragmentSize];
  126. Array.Copy(this.Data, 0, newData, 0, maxFragmentSize);
  127. this.Data = newData;
  128. return fragments;
  129. }
  130. #endregion
  131. }
  132. }
  133. #endif