HelperClasses.cs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. #if !BESTHTTP_DISABLE_SIGNALR_CORE
  2. using System;
  3. using System.Collections.Generic;
  4. using BestHTTP.PlatformSupport.Memory;
  5. namespace BestHTTP.SignalRCore
  6. {
  7. public enum TransportTypes
  8. {
  9. #if !BESTHTTP_DISABLE_WEBSOCKET
  10. WebSocket,
  11. #endif
  12. LongPolling
  13. }
  14. public enum TransferModes
  15. {
  16. Binary,
  17. Text
  18. }
  19. public enum TransportStates
  20. {
  21. Initial,
  22. Connecting,
  23. Connected,
  24. Closing,
  25. Failed,
  26. Closed
  27. }
  28. /// <summary>
  29. /// Possible states of a HubConnection
  30. /// </summary>
  31. public enum ConnectionStates
  32. {
  33. Initial,
  34. Authenticating,
  35. Negotiating,
  36. Redirected,
  37. Reconnecting,
  38. Connected,
  39. CloseInitiated,
  40. Closed
  41. }
  42. /// <summary>
  43. /// States that a transport can goes trough as seen from 'outside'.
  44. /// </summary>
  45. public enum TransportEvents
  46. {
  47. /// <summary>
  48. /// Transport is selected to try to connect to the server
  49. /// </summary>
  50. SelectedToConnect,
  51. /// <summary>
  52. /// Transport failed to connect to the server. This event can occur after SelectedToConnect, when already connected and an error occurs it will be a ClosedWithError one.
  53. /// </summary>
  54. FailedToConnect,
  55. /// <summary>
  56. /// The transport successfully connected to the server.
  57. /// </summary>
  58. Connected,
  59. /// <summary>
  60. /// Transport gracefully terminated.
  61. /// </summary>
  62. Closed,
  63. /// <summary>
  64. /// Unexpected error occured and the transport can't recover from it.
  65. /// </summary>
  66. ClosedWithError
  67. }
  68. public interface ITransport
  69. {
  70. TransferModes TransferMode { get; }
  71. TransportTypes TransportType { get; }
  72. TransportStates State { get; }
  73. string ErrorReason { get; }
  74. event Action<TransportStates, TransportStates> OnStateChanged;
  75. void StartConnect();
  76. void StartClose();
  77. void Send(BufferSegment bufferSegment);
  78. }
  79. public interface IEncoder
  80. {
  81. BufferSegment Encode<T>(T value);
  82. T DecodeAs<T>(BufferSegment buffer);
  83. object ConvertTo(Type toType, object obj);
  84. }
  85. public sealed class StreamItemContainer<T>
  86. {
  87. public readonly long id;
  88. public List<T> Items { get; private set; }
  89. public T LastAdded { get; private set; }
  90. public bool IsCanceled;
  91. public StreamItemContainer(long _id)
  92. {
  93. this.id = _id;
  94. this.Items = new List<T>();
  95. }
  96. public void AddItem(T item)
  97. {
  98. if (this.Items == null)
  99. this.Items = new List<T>();
  100. this.Items.Add(item);
  101. this.LastAdded = item;
  102. }
  103. }
  104. struct CallbackDescriptor
  105. {
  106. public readonly Type[] ParamTypes;
  107. public readonly Action<object[]> Callback;
  108. public CallbackDescriptor(Type[] paramTypes, Action<object[]> callback)
  109. {
  110. this.ParamTypes = paramTypes;
  111. this.Callback = callback;
  112. }
  113. }
  114. internal struct InvocationDefinition
  115. {
  116. public Action<Messages.Message> callback;
  117. public Type returnType;
  118. }
  119. internal sealed class Subscription
  120. {
  121. public List<CallbackDescriptor> callbacks = new List<CallbackDescriptor>(1);
  122. public void Add(Type[] paramTypes, Action<object[]> callback)
  123. {
  124. this.callbacks.Add(new CallbackDescriptor(paramTypes, callback));
  125. }
  126. public void Remove(Action<object[]> callback)
  127. {
  128. int idx = -1;
  129. for (int i = 0; i < this.callbacks.Count && idx == -1; ++i)
  130. if (this.callbacks[i].Callback == callback)
  131. idx = i;
  132. if (idx != -1)
  133. this.callbacks.RemoveAt(idx);
  134. }
  135. }
  136. public sealed class HubOptions
  137. {
  138. /// <summary>
  139. /// When this is set to true, the plugin will skip the negotiation request if the PreferedTransport is WebSocket. Its default value is false.
  140. /// </summary>
  141. public bool SkipNegotiation { get; set; }
  142. /// <summary>
  143. /// The preferred transport to choose when more than one available. Its default value is TransportTypes.WebSocket.
  144. /// </summary>
  145. public TransportTypes PreferedTransport { get; set; }
  146. /// <summary>
  147. /// A ping message is only sent if the interval has elapsed without a message being sent. Its default value is 15 seconds.
  148. /// </summary>
  149. public TimeSpan PingInterval { get; set; }
  150. /// <summary>
  151. /// If the client doesn't see any message in this interval, considers the connection broken. Its default value is 30 seconds.
  152. /// </summary>
  153. public TimeSpan PingTimeoutInterval { get; set; }
  154. /// <summary>
  155. /// The maximum count of redirect negotiation result that the plugin will follow. Its default value is 100.
  156. /// </summary>
  157. public int MaxRedirects { get; set; }
  158. /// <summary>
  159. /// The maximum time that the plugin allowed to spend trying to connect. Its default value is 1 minute.
  160. /// </summary>
  161. public TimeSpan ConnectTimeout { get; set; }
  162. public HubOptions()
  163. {
  164. this.SkipNegotiation = false;
  165. #if !BESTHTTP_DISABLE_WEBSOCKET
  166. this.PreferedTransport = TransportTypes.WebSocket;
  167. #else
  168. this.PreferedTransport = TransportTypes.LongPolling;
  169. #endif
  170. this.PingInterval = TimeSpan.FromSeconds(15);
  171. this.PingTimeoutInterval = TimeSpan.FromSeconds(30);
  172. this.MaxRedirects = 100;
  173. this.ConnectTimeout = TimeSpan.FromSeconds(60);
  174. }
  175. }
  176. public interface IRetryPolicy
  177. {
  178. /// <summary>
  179. /// This function must return with a delay time to wait until a new connection attempt, or null to do not do another one.
  180. /// </summary>
  181. TimeSpan? GetNextRetryDelay(RetryContext context);
  182. }
  183. public struct RetryContext
  184. {
  185. /// <summary>
  186. /// Previous reconnect attempts. A successful connection sets it back to zero.
  187. /// </summary>
  188. public uint PreviousRetryCount;
  189. /// <summary>
  190. /// Elapsed time since the original connection error.
  191. /// </summary>
  192. public TimeSpan ElapsedTime;
  193. /// <summary>
  194. /// String representation of the connection error.
  195. /// </summary>
  196. public string RetryReason;
  197. }
  198. public sealed class DefaultRetryPolicy : IRetryPolicy
  199. {
  200. private static TimeSpan?[] DefaultBackoffTimes = new TimeSpan?[]
  201. {
  202. TimeSpan.Zero,
  203. TimeSpan.FromSeconds(2),
  204. TimeSpan.FromSeconds(10),
  205. TimeSpan.FromSeconds(30),
  206. null
  207. };
  208. TimeSpan?[] backoffTimes;
  209. public DefaultRetryPolicy()
  210. {
  211. this.backoffTimes = DefaultBackoffTimes;
  212. }
  213. public DefaultRetryPolicy(TimeSpan?[] customBackoffTimes)
  214. {
  215. this.backoffTimes = customBackoffTimes;
  216. }
  217. public TimeSpan? GetNextRetryDelay(RetryContext context)
  218. {
  219. if (context.PreviousRetryCount >= this.backoffTimes.Length)
  220. return null;
  221. return this.backoffTimes[context.PreviousRetryCount];
  222. }
  223. }
  224. }
  225. #endif