MessageProcessor.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. using System;
  2. using System.IO;
  3. using System.Runtime.CompilerServices;
  4. namespace Mirror.SimpleWeb
  5. {
  6. public static class MessageProcessor
  7. {
  8. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  9. static byte FirstLengthByte(byte[] buffer) => (byte)(buffer[1] & 0b0111_1111);
  10. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  11. public static bool NeedToReadShortLength(byte[] buffer)
  12. {
  13. byte lenByte = FirstLengthByte(buffer);
  14. return lenByte == Constants.UshortPayloadLength;
  15. }
  16. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  17. public static bool NeedToReadLongLength(byte[] buffer)
  18. {
  19. byte lenByte = FirstLengthByte(buffer);
  20. return lenByte == Constants.UlongPayloadLength;
  21. }
  22. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  23. public static int GetOpcode(byte[] buffer)
  24. {
  25. return buffer[0] & 0b0000_1111;
  26. }
  27. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  28. public static int GetPayloadLength(byte[] buffer)
  29. {
  30. byte lenByte = FirstLengthByte(buffer);
  31. return GetMessageLength(buffer, 0, lenByte);
  32. }
  33. /// <summary>
  34. /// Has full message been sent
  35. /// </summary>
  36. /// <param name="buffer"></param>
  37. /// <returns></returns>
  38. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  39. public static bool Finished(byte[] buffer)
  40. {
  41. return (buffer[0] & 0b1000_0000) != 0;
  42. }
  43. public static void ValidateHeader(byte[] buffer, int maxLength, bool expectMask, bool opCodeContinuation = false)
  44. {
  45. bool finished = Finished(buffer);
  46. bool hasMask = (buffer[1] & 0b1000_0000) != 0; // true from clients, false from server, "All messages from the client to the server have this bit set"
  47. int opcode = buffer[0] & 0b0000_1111; // expecting 1 - text message
  48. byte lenByte = FirstLengthByte(buffer);
  49. ThrowIfMaskNotExpected(hasMask, expectMask);
  50. ThrowIfBadOpCode(opcode, finished, opCodeContinuation);
  51. int msglen = GetMessageLength(buffer, 0, lenByte);
  52. ThrowIfLengthZero(msglen);
  53. ThrowIfMsgLengthTooLong(msglen, maxLength);
  54. }
  55. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  56. public static void ToggleMask(byte[] src, int sourceOffset, int messageLength, byte[] maskBuffer, int maskOffset)
  57. {
  58. ToggleMask(src, sourceOffset, src, sourceOffset, messageLength, maskBuffer, maskOffset);
  59. }
  60. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  61. public static void ToggleMask(byte[] src, int sourceOffset, ArrayBuffer dst, int messageLength, byte[] maskBuffer, int maskOffset)
  62. {
  63. ToggleMask(src, sourceOffset, dst.array, 0, messageLength, maskBuffer, maskOffset);
  64. dst.count = messageLength;
  65. }
  66. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  67. public static void ToggleMask(byte[] src, int srcOffset, byte[] dst, int dstOffset, int messageLength, byte[] maskBuffer, int maskOffset)
  68. {
  69. for (int i = 0; i < messageLength; i++)
  70. {
  71. byte maskByte = maskBuffer[maskOffset + i % Constants.MaskSize];
  72. dst[dstOffset + i] = (byte)(src[srcOffset + i] ^ maskByte);
  73. }
  74. }
  75. /// <exception cref="InvalidDataException"></exception>
  76. static int GetMessageLength(byte[] buffer, int offset, byte lenByte)
  77. {
  78. if (lenByte == Constants.UshortPayloadLength)
  79. {
  80. // header is 2 bytes
  81. ushort value = 0;
  82. value |= (ushort)(buffer[offset + 2] << 8);
  83. value |= buffer[offset + 3];
  84. return value;
  85. }
  86. else if (lenByte == Constants.UlongPayloadLength)
  87. {
  88. // header is 8 bytes
  89. ulong value = 0;
  90. value |= ((ulong)buffer[offset + 2] << 56);
  91. value |= ((ulong)buffer[offset + 3] << 48);
  92. value |= ((ulong)buffer[offset + 4] << 40);
  93. value |= ((ulong)buffer[offset + 5] << 32);
  94. value |= ((ulong)buffer[offset + 6] << 24);
  95. value |= ((ulong)buffer[offset + 7] << 16);
  96. value |= ((ulong)buffer[offset + 8] << 8);
  97. value |= ((ulong)buffer[offset + 9] << 0);
  98. if (value > int.MaxValue)
  99. {
  100. throw new NotSupportedException($"Can't receive payloads larger that int.max: {int.MaxValue}");
  101. }
  102. return (int)value;
  103. }
  104. else // is less than 126
  105. {
  106. // header is 2 bytes long
  107. return lenByte;
  108. }
  109. }
  110. /// <exception cref="InvalidDataException"></exception>
  111. static void ThrowIfMaskNotExpected(bool hasMask, bool expectMask)
  112. {
  113. if (hasMask != expectMask)
  114. {
  115. throw new InvalidDataException($"Message expected mask to be {expectMask} but was {hasMask}");
  116. }
  117. }
  118. /// <exception cref="InvalidDataException"></exception>
  119. static void ThrowIfBadOpCode(int opcode, bool finished, bool opCodeContinuation)
  120. {
  121. // 0 = continuation
  122. // 2 = binary
  123. // 8 = close
  124. // do we expect Continuation?
  125. if (opCodeContinuation)
  126. {
  127. // good it was Continuation
  128. if (opcode == 0)
  129. return;
  130. // bad, wasn't Continuation
  131. throw new InvalidDataException("Expected opcode to be Continuation");
  132. }
  133. else if (!finished)
  134. {
  135. // fragmented message, should be binary
  136. if (opcode == 2)
  137. return;
  138. throw new InvalidDataException("Expected opcode to be binary");
  139. }
  140. else
  141. {
  142. // normal message, should be binary or close
  143. if (opcode == 2 || opcode == 8)
  144. return;
  145. throw new InvalidDataException("Expected opcode to be binary or close");
  146. }
  147. }
  148. /// <exception cref="InvalidDataException"></exception>
  149. static void ThrowIfLengthZero(int msglen)
  150. {
  151. if (msglen == 0)
  152. {
  153. throw new InvalidDataException("Message length was zero");
  154. }
  155. }
  156. /// <summary>
  157. /// need to check this so that data from previous buffer isn't used
  158. /// </summary>
  159. public static void ThrowIfMsgLengthTooLong(int msglen, int maxLength)
  160. {
  161. if (msglen > maxLength)
  162. {
  163. throw new InvalidDataException("Message length is greater than max length");
  164. }
  165. }
  166. }
  167. }