JsonProtocol.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. #if !BESTHTTP_DISABLE_SIGNALR_CORE && !BESTHTTP_DISABLE_WEBSOCKET
  2. using BestHTTP.SignalRCore.Messages;
  3. using System;
  4. using System.Collections.Generic;
  5. #if NETFX_CORE || NET_4_6
  6. using System.Reflection;
  7. #endif
  8. namespace BestHTTP.SignalRCore
  9. {
  10. public interface IProtocol
  11. {
  12. TransferModes Type { get; }
  13. IEncoder Encoder { get; }
  14. HubConnection Connection { get; set; }
  15. /// <summary>
  16. /// This function must parse textual representation of the messages into the list of Messages.
  17. /// </summary>
  18. void ParseMessages(string data, ref List<Message> messages);
  19. /// <summary>
  20. /// This function must parse binary representation of the messages into the list of Messages.
  21. /// </summary>
  22. void ParseMessages(byte[] data, ref List<Message> messages);
  23. /// <summary>
  24. /// This function must return the encoded representation of the given message.
  25. /// </summary>
  26. byte[] EncodeMessage(Message message);
  27. /// <summary>
  28. /// This function must convert all element in the arguments array to the corresponding type from the argTypes array.
  29. /// </summary>
  30. object[] GetRealArguments(Type[] argTypes, object[] arguments);
  31. /// <summary>
  32. /// Convert a value to the given type.
  33. /// </summary>
  34. object ConvertTo(Type toType, object obj);
  35. }
  36. public sealed class JsonProtocol : IProtocol
  37. {
  38. public const char Separator = (char)0x1E;
  39. public TransferModes Type { get { return TransferModes.Text; } }
  40. public IEncoder Encoder { get; private set; }
  41. public HubConnection Connection { get; set; }
  42. public JsonProtocol(IEncoder encoder)
  43. {
  44. if (encoder == null)
  45. throw new ArgumentNullException("encoder");
  46. if (encoder.Name != "json")
  47. throw new ArgumentException("Encoder must be a json encoder!");
  48. this.Encoder = encoder;
  49. }
  50. public void ParseMessages(string data, ref List<Message> messages)
  51. {
  52. int from = 0;
  53. int separatorIdx = data.IndexOf(JsonProtocol.Separator);
  54. if (separatorIdx == -1)
  55. throw new Exception("Missing separator!");
  56. while (separatorIdx != -1)
  57. {
  58. string sub = data.Substring(from, separatorIdx - from);
  59. var message = this.Encoder.DecodeAs<Message>(sub);
  60. messages.Add(message);
  61. from = separatorIdx + 1;
  62. separatorIdx = data.IndexOf(JsonProtocol.Separator, from);
  63. }
  64. }
  65. public void ParseMessages(byte[] data, ref List<Message> messages) { }
  66. public byte[] EncodeMessage(Message message)
  67. {
  68. string json = null;
  69. switch (message.type)
  70. {
  71. case MessageTypes.Invocation:
  72. case MessageTypes.StreamInvocation:
  73. // While message contains all informations already, the spec states that no additional field are allowed in messages
  74. // So we are creating 'specialized' messages here to send to the server.
  75. json = this.Encoder.EncodeAsText<InvocationMessage>(new InvocationMessage()
  76. {
  77. type = message.type,
  78. invocationId = message.invocationId,
  79. nonblocking = message.nonblocking,
  80. target = message.target,
  81. arguments = message.arguments
  82. });
  83. break;
  84. case MessageTypes.CancelInvocation:
  85. json = this.Encoder.EncodeAsText<CancelInvocationMessage>(new CancelInvocationMessage()
  86. {
  87. invocationId = message.invocationId
  88. });
  89. break;
  90. }
  91. return !string.IsNullOrEmpty(json) ? JsonProtocol.WithSeparator(json) : null;
  92. }
  93. public object[] GetRealArguments(Type[] argTypes, object[] arguments)
  94. {
  95. if (arguments == null || arguments.Length == 0)
  96. return null;
  97. if (argTypes.Length > arguments.Length)
  98. throw new Exception(string.Format("argType.Length({0}) < arguments.length({1})", argTypes.Length, arguments.Length));
  99. object[] realArgs = new object[arguments.Length];
  100. for (int i = 0; i < arguments.Length; ++i)
  101. realArgs[i] = ConvertTo(argTypes[i], arguments[i]);
  102. return realArgs;
  103. }
  104. public object ConvertTo(Type toType, object obj)
  105. {
  106. if (obj == null)
  107. return null;
  108. #if NETFX_CORE //|| NET_4_6
  109. if (toType.GetTypeInfo().IsPrimitive || toType.GetTypeInfo().IsEnum)
  110. #else
  111. if (toType.IsPrimitive || toType.IsEnum)
  112. #endif
  113. return Convert.ChangeType(obj, toType);
  114. if (toType == typeof(string))
  115. return obj.ToString();
  116. return this.Encoder.ConvertTo(toType, obj);
  117. }
  118. /// <summary>
  119. /// Returns the given string parameter's bytes with the added separator(0x1E).
  120. /// </summary>
  121. public static byte[] WithSeparator(string str)
  122. {
  123. int len = System.Text.Encoding.UTF8.GetByteCount(str);
  124. byte[] buffer = new byte[len + 1];
  125. System.Text.Encoding.UTF8.GetBytes(str, 0, str.Length, buffer, 0);
  126. buffer[len] = 0x1e;
  127. return buffer;
  128. }
  129. }
  130. }
  131. #endif