JsonProtocol.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. #if !BESTHTTP_DISABLE_SIGNALR_CORE
  2. using BestHTTP.PlatformSupport.Memory;
  3. using BestHTTP.SignalRCore.Messages;
  4. using System;
  5. using System.Collections.Generic;
  6. #if NETFX_CORE || NET_4_6
  7. using System.Reflection;
  8. #endif
  9. namespace BestHTTP.SignalRCore
  10. {
  11. public interface IProtocol
  12. {
  13. string Name { get; }
  14. TransferModes Type { get; }
  15. IEncoder Encoder { get; }
  16. HubConnection Connection { get; set; }
  17. /// <summary>
  18. /// This function must parse binary representation of the messages into the list of Messages.
  19. /// </summary>
  20. void ParseMessages(BufferSegment segment, ref List<Message> messages);
  21. /// <summary>
  22. /// This function must return the encoded representation of the given message.
  23. /// </summary>
  24. BufferSegment EncodeMessage(Message message);
  25. /// <summary>
  26. /// This function must convert all element in the arguments array to the corresponding type from the argTypes array.
  27. /// </summary>
  28. object[] GetRealArguments(Type[] argTypes, object[] arguments);
  29. /// <summary>
  30. /// Convert a value to the given type.
  31. /// </summary>
  32. object ConvertTo(Type toType, object obj);
  33. }
  34. public sealed class JsonProtocol : IProtocol
  35. {
  36. public const char Separator = (char)0x1E;
  37. public string Name { get { return "json"; } }
  38. public TransferModes Type { get { return TransferModes.Binary; } }
  39. public IEncoder Encoder { get; private set; }
  40. public HubConnection Connection { get; set; }
  41. public JsonProtocol(IEncoder encoder)
  42. {
  43. if (encoder == null)
  44. throw new ArgumentNullException("encoder");
  45. this.Encoder = encoder;
  46. }
  47. public void ParseMessages(BufferSegment segment, ref List<Message> messages) {
  48. if (segment.Data == null || segment.Count == 0)
  49. return;
  50. int from = segment.Offset;
  51. int separatorIdx = Array.IndexOf<byte>(segment.Data, (byte)JsonProtocol.Separator, from);
  52. if (separatorIdx == -1)
  53. throw new Exception("Missing separator in data! Segment: " + segment.ToString());
  54. while (separatorIdx != -1)
  55. {
  56. if (HTTPManager.Logger.Level == Logger.Loglevels.All)
  57. HTTPManager.Logger.Verbose("JsonProtocol", "ParseMessages - " + System.Text.Encoding.UTF8.GetString(segment.Data, from, separatorIdx - from));
  58. var message = this.Encoder.DecodeAs<Message>(new BufferSegment(segment.Data, from, separatorIdx - from));
  59. messages.Add(message);
  60. from = separatorIdx + 1;
  61. separatorIdx = Array.IndexOf<byte>(segment.Data, (byte)JsonProtocol.Separator, from);
  62. }
  63. }
  64. public BufferSegment EncodeMessage(Message message)
  65. {
  66. BufferSegment result = BufferSegment.Empty;
  67. // While message contains all informations already, the spec states that no additional field are allowed in messages
  68. // So we are creating 'specialized' messages here to send to the server.
  69. switch (message.type)
  70. {
  71. case MessageTypes.StreamItem:
  72. result = this.Encoder.Encode<StreamItemMessage>(new StreamItemMessage()
  73. {
  74. type = message.type,
  75. invocationId = message.invocationId,
  76. item = message.item
  77. });
  78. break;
  79. case MessageTypes.Completion:
  80. if (!string.IsNullOrEmpty(message.error))
  81. {
  82. result = this.Encoder.Encode<CompletionWithError>(new CompletionWithError()
  83. {
  84. type = MessageTypes.Completion,
  85. invocationId = message.invocationId,
  86. error = message.error
  87. });
  88. }
  89. else if (message.result != null)
  90. {
  91. result = this.Encoder.Encode<CompletionWithResult>(new CompletionWithResult()
  92. {
  93. type = MessageTypes.Completion,
  94. invocationId = message.invocationId,
  95. result = message.result
  96. });
  97. }
  98. else
  99. result = this.Encoder.Encode<Completion>(new Completion()
  100. {
  101. type = MessageTypes.Completion,
  102. invocationId = message.invocationId
  103. });
  104. break;
  105. case MessageTypes.Invocation:
  106. case MessageTypes.StreamInvocation:
  107. if (message.streamIds != null)
  108. {
  109. result = this.Encoder.Encode<UploadInvocationMessage>(new UploadInvocationMessage()
  110. {
  111. type = message.type,
  112. invocationId = message.invocationId,
  113. nonblocking = message.nonblocking,
  114. target = message.target,
  115. arguments = message.arguments,
  116. streamIds = message.streamIds
  117. });
  118. }
  119. else
  120. {
  121. result = this.Encoder.Encode<InvocationMessage>(new InvocationMessage()
  122. {
  123. type = message.type,
  124. invocationId = message.invocationId,
  125. nonblocking = message.nonblocking,
  126. target = message.target,
  127. arguments = message.arguments
  128. });
  129. }
  130. break;
  131. case MessageTypes.CancelInvocation:
  132. result = this.Encoder.Encode<CancelInvocationMessage>(new CancelInvocationMessage()
  133. {
  134. invocationId = message.invocationId
  135. });
  136. break;
  137. case MessageTypes.Ping:
  138. result = this.Encoder.Encode<PingMessage>(new PingMessage());
  139. break;
  140. case MessageTypes.Close:
  141. if (!string.IsNullOrEmpty(message.error))
  142. result = this.Encoder.Encode<CloseWithErrorMessage>(new CloseWithErrorMessage() { error = message.error });
  143. else
  144. result = this.Encoder.Encode<CloseMessage>(new CloseMessage());
  145. break;
  146. }
  147. if (HTTPManager.Logger.Level == Logger.Loglevels.All)
  148. HTTPManager.Logger.Verbose("JsonProtocol", "EncodeMessage - json: " + System.Text.Encoding.UTF8.GetString(result.Data, 0, result.Count - 1));
  149. return result;
  150. }
  151. public object[] GetRealArguments(Type[] argTypes, object[] arguments)
  152. {
  153. if (arguments == null || arguments.Length == 0)
  154. return null;
  155. if (argTypes.Length > arguments.Length)
  156. throw new Exception(string.Format("argType.Length({0}) < arguments.length({1})", argTypes.Length, arguments.Length));
  157. object[] realArgs = new object[arguments.Length];
  158. for (int i = 0; i < arguments.Length; ++i)
  159. realArgs[i] = ConvertTo(argTypes[i], arguments[i]);
  160. return realArgs;
  161. }
  162. public object ConvertTo(Type toType, object obj)
  163. {
  164. if (obj == null)
  165. return null;
  166. #if NETFX_CORE
  167. TypeInfo typeInfo = toType.GetTypeInfo();
  168. #endif
  169. #if NETFX_CORE
  170. if (typeInfo.IsEnum)
  171. #else
  172. if (toType.IsEnum)
  173. #endif
  174. return Enum.Parse(toType, obj.ToString(), true);
  175. #if NETFX_CORE
  176. if (typeInfo.IsPrimitive)
  177. #else
  178. if (toType.IsPrimitive)
  179. #endif
  180. return Convert.ChangeType(obj, toType);
  181. if (toType == typeof(string))
  182. return obj.ToString();
  183. #if NETFX_CORE
  184. if (typeInfo.IsGenericType && toType.Name == "Nullable`1")
  185. return Convert.ChangeType(obj, toType.GenericTypeArguments[0]);
  186. #else
  187. if (toType.IsGenericType && toType.Name == "Nullable`1")
  188. return Convert.ChangeType(obj, toType.GetGenericArguments()[0]);
  189. #endif
  190. return this.Encoder.ConvertTo(toType, obj);
  191. }
  192. /// <summary>
  193. /// Returns the given string parameter's bytes with the added separator(0x1E).
  194. /// </summary>
  195. public static BufferSegment WithSeparator(string str)
  196. {
  197. int len = System.Text.Encoding.UTF8.GetByteCount(str);
  198. byte[] buffer = BufferPool.Get(len + 1, true);
  199. System.Text.Encoding.UTF8.GetBytes(str, 0, str.Length, buffer, 0);
  200. buffer[len] = 0x1e;
  201. return new BufferSegment(buffer, 0, len + 1);
  202. }
  203. }
  204. }
  205. #endif