HelperClasses.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  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. struct FunctionCallbackDescriptor
  115. {
  116. public readonly Type ReturnType;
  117. public readonly Type[] ParamTypes;
  118. public readonly Func<object[], object> Callback;
  119. public FunctionCallbackDescriptor(Type returnType, Type[] paramTypes, Func<object[], object> callback)
  120. {
  121. this.ReturnType = returnType;
  122. this.ParamTypes = paramTypes;
  123. this.Callback = callback;
  124. }
  125. }
  126. internal struct InvocationDefinition
  127. {
  128. public Action<Messages.Message> callback;
  129. public Type returnType;
  130. }
  131. internal sealed class Subscription
  132. {
  133. public List<CallbackDescriptor> callbacks = new List<CallbackDescriptor>(1);
  134. public List<FunctionCallbackDescriptor> functionCallbacks;
  135. public void Add(Type[] paramTypes, Action<object[]> callback)
  136. {
  137. this.callbacks.Add(new CallbackDescriptor(paramTypes, callback));
  138. }
  139. public void AddFunc(Type resultType, Type[] paramTypes, Func<object[], object> callback)
  140. {
  141. if (this.functionCallbacks == null)
  142. this.functionCallbacks = new List<FunctionCallbackDescriptor>(1);
  143. this.functionCallbacks.Add(new FunctionCallbackDescriptor(resultType, paramTypes, callback));
  144. }
  145. }
  146. public sealed class WebsocketOptions
  147. {
  148. #if !BESTHTTP_DISABLE_WEBSOCKET && (!UNITY_WEBGL || UNITY_EDITOR)
  149. public Func<WebSocket.Extensions.IExtension[]> ExtensionsFactory { get; set; } = WebSocket.WebSocket.GetDefaultExtensions;
  150. public TimeSpan? PingIntervalOverride { get; set; } = TimeSpan.Zero;
  151. #endif
  152. }
  153. public sealed class HubOptions
  154. {
  155. /// <summary>
  156. /// When this is set to true, the plugin will skip the negotiation request if the PreferedTransport is WebSocket. Its default value is false.
  157. /// </summary>
  158. public bool SkipNegotiation { get; set; }
  159. /// <summary>
  160. /// The preferred transport to choose when more than one available. Its default value is TransportTypes.WebSocket.
  161. /// </summary>
  162. public TransportTypes PreferedTransport { get; set; }
  163. /// <summary>
  164. /// A ping message is only sent if the interval has elapsed without a message being sent. Its default value is 15 seconds.
  165. /// </summary>
  166. public TimeSpan PingInterval { get; set; }
  167. /// <summary>
  168. /// If the client doesn't see any message in this interval, considers the connection broken. Its default value is 30 seconds.
  169. /// </summary>
  170. public TimeSpan PingTimeoutInterval { get; set; }
  171. /// <summary>
  172. /// The maximum count of redirect negotiation result that the plugin will follow. Its default value is 100.
  173. /// </summary>
  174. public int MaxRedirects { get; set; }
  175. /// <summary>
  176. /// The maximum time that the plugin allowed to spend trying to connect. Its default value is 1 minute.
  177. /// </summary>
  178. public TimeSpan ConnectTimeout { get; set; }
  179. /// <summary>
  180. /// Customization options for the websocket transport.
  181. /// </summary>
  182. public WebsocketOptions WebsocketOptions { get; set; } = new WebsocketOptions();
  183. public HubOptions()
  184. {
  185. this.SkipNegotiation = false;
  186. #if !BESTHTTP_DISABLE_WEBSOCKET
  187. this.PreferedTransport = TransportTypes.WebSocket;
  188. #else
  189. this.PreferedTransport = TransportTypes.LongPolling;
  190. #endif
  191. this.PingInterval = TimeSpan.FromSeconds(15);
  192. this.PingTimeoutInterval = TimeSpan.FromSeconds(30);
  193. this.MaxRedirects = 100;
  194. this.ConnectTimeout = TimeSpan.FromSeconds(60);
  195. }
  196. }
  197. public interface IRetryPolicy
  198. {
  199. /// <summary>
  200. /// This function must return with a delay time to wait until a new connection attempt, or null to do not do another one.
  201. /// </summary>
  202. TimeSpan? GetNextRetryDelay(RetryContext context);
  203. }
  204. public struct RetryContext
  205. {
  206. /// <summary>
  207. /// Previous reconnect attempts. A successful connection sets it back to zero.
  208. /// </summary>
  209. public uint PreviousRetryCount;
  210. /// <summary>
  211. /// Elapsed time since the original connection error.
  212. /// </summary>
  213. public TimeSpan ElapsedTime;
  214. /// <summary>
  215. /// String representation of the connection error.
  216. /// </summary>
  217. public string RetryReason;
  218. }
  219. public sealed class DefaultRetryPolicy : IRetryPolicy
  220. {
  221. private static TimeSpan?[] DefaultBackoffTimes = new TimeSpan?[]
  222. {
  223. TimeSpan.Zero,
  224. TimeSpan.FromSeconds(2),
  225. TimeSpan.FromSeconds(10),
  226. TimeSpan.FromSeconds(30),
  227. null
  228. };
  229. TimeSpan?[] backoffTimes;
  230. public DefaultRetryPolicy()
  231. {
  232. this.backoffTimes = DefaultBackoffTimes;
  233. }
  234. public DefaultRetryPolicy(TimeSpan?[] customBackoffTimes)
  235. {
  236. this.backoffTimes = customBackoffTimes;
  237. }
  238. public TimeSpan? GetNextRetryDelay(RetryContext context)
  239. {
  240. if (context.PreviousRetryCount >= this.backoffTimes.Length)
  241. return null;
  242. return this.backoffTimes[context.PreviousRetryCount];
  243. }
  244. }
  245. }
  246. #endif