You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
271 lines
7.3 KiB
271 lines
7.3 KiB
#if !BESTHTTP_DISABLE_SIGNALR_CORE |
|
using System; |
|
using System.Collections.Generic; |
|
using BestHTTP.PlatformSupport.Memory; |
|
|
|
namespace BestHTTP.SignalRCore |
|
{ |
|
public enum TransportTypes |
|
{ |
|
#if !BESTHTTP_DISABLE_WEBSOCKET |
|
WebSocket, |
|
#endif |
|
LongPolling |
|
} |
|
|
|
public enum TransferModes |
|
{ |
|
Binary, |
|
Text |
|
} |
|
|
|
public enum TransportStates |
|
{ |
|
Initial, |
|
Connecting, |
|
Connected, |
|
Closing, |
|
Failed, |
|
Closed |
|
} |
|
|
|
/// <summary> |
|
/// Possible states of a HubConnection |
|
/// </summary> |
|
public enum ConnectionStates |
|
{ |
|
Initial, |
|
Authenticating, |
|
Negotiating, |
|
Redirected, |
|
Reconnecting, |
|
Connected, |
|
CloseInitiated, |
|
Closed |
|
} |
|
|
|
/// <summary> |
|
/// States that a transport can goes trough as seen from 'outside'. |
|
/// </summary> |
|
public enum TransportEvents |
|
{ |
|
/// <summary> |
|
/// Transport is selected to try to connect to the server |
|
/// </summary> |
|
SelectedToConnect, |
|
|
|
/// <summary> |
|
/// 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. |
|
/// </summary> |
|
FailedToConnect, |
|
|
|
/// <summary> |
|
/// The transport successfully connected to the server. |
|
/// </summary> |
|
Connected, |
|
|
|
/// <summary> |
|
/// Transport gracefully terminated. |
|
/// </summary> |
|
Closed, |
|
|
|
/// <summary> |
|
/// Unexpected error occured and the transport can't recover from it. |
|
/// </summary> |
|
ClosedWithError |
|
} |
|
|
|
public interface ITransport |
|
{ |
|
TransferModes TransferMode { get; } |
|
TransportTypes TransportType { get; } |
|
TransportStates State { get; } |
|
|
|
string ErrorReason { get; } |
|
|
|
event Action<TransportStates, TransportStates> OnStateChanged; |
|
|
|
void StartConnect(); |
|
void StartClose(); |
|
|
|
void Send(BufferSegment bufferSegment); |
|
} |
|
|
|
public interface IEncoder |
|
{ |
|
BufferSegment Encode<T>(T value); |
|
|
|
T DecodeAs<T>(BufferSegment buffer); |
|
|
|
object ConvertTo(Type toType, object obj); |
|
} |
|
|
|
public sealed class StreamItemContainer<T> |
|
{ |
|
public readonly long id; |
|
|
|
public List<T> Items { get; private set; } |
|
public T LastAdded { get; private set; } |
|
|
|
public bool IsCanceled; |
|
|
|
public StreamItemContainer(long _id) |
|
{ |
|
this.id = _id; |
|
this.Items = new List<T>(); |
|
} |
|
|
|
public void AddItem(T item) |
|
{ |
|
if (this.Items == null) |
|
this.Items = new List<T>(); |
|
|
|
this.Items.Add(item); |
|
this.LastAdded = item; |
|
} |
|
} |
|
|
|
struct CallbackDescriptor |
|
{ |
|
public readonly Type[] ParamTypes; |
|
public readonly Action<object[]> Callback; |
|
public CallbackDescriptor(Type[] paramTypes, Action<object[]> callback) |
|
{ |
|
this.ParamTypes = paramTypes; |
|
this.Callback = callback; |
|
} |
|
} |
|
|
|
internal struct InvocationDefinition |
|
{ |
|
public Action<Messages.Message> callback; |
|
public Type returnType; |
|
} |
|
|
|
internal sealed class Subscription |
|
{ |
|
public List<CallbackDescriptor> callbacks = new List<CallbackDescriptor>(1); |
|
|
|
public void Add(Type[] paramTypes, Action<object[]> callback) |
|
{ |
|
this.callbacks.Add(new CallbackDescriptor(paramTypes, callback)); |
|
} |
|
|
|
public void Remove(Action<object[]> callback) |
|
{ |
|
int idx = -1; |
|
for (int i = 0; i < this.callbacks.Count && idx == -1; ++i) |
|
if (this.callbacks[i].Callback == callback) |
|
idx = i; |
|
|
|
if (idx != -1) |
|
this.callbacks.RemoveAt(idx); |
|
} |
|
} |
|
|
|
public sealed class HubOptions |
|
{ |
|
/// <summary> |
|
/// When this is set to true, the plugin will skip the negotiation request if the PreferedTransport is WebSocket. Its default value is false. |
|
/// </summary> |
|
public bool SkipNegotiation { get; set; } |
|
|
|
/// <summary> |
|
/// The preferred transport to choose when more than one available. Its default value is TransportTypes.WebSocket. |
|
/// </summary> |
|
public TransportTypes PreferedTransport { get; set; } |
|
|
|
/// <summary> |
|
/// A ping message is only sent if the interval has elapsed without a message being sent. Its default value is 15 seconds. |
|
/// </summary> |
|
public TimeSpan PingInterval { get; set; } |
|
|
|
/// <summary> |
|
/// If the client doesn't see any message in this interval, considers the connection broken. Its default value is 30 seconds. |
|
/// </summary> |
|
public TimeSpan PingTimeoutInterval { get; set; } |
|
|
|
/// <summary> |
|
/// The maximum count of redirect negotiation result that the plugin will follow. Its default value is 100. |
|
/// </summary> |
|
public int MaxRedirects { get; set; } |
|
|
|
/// <summary> |
|
/// The maximum time that the plugin allowed to spend trying to connect. Its default value is 1 minute. |
|
/// </summary> |
|
public TimeSpan ConnectTimeout { get; set; } |
|
|
|
public HubOptions() |
|
{ |
|
this.SkipNegotiation = false; |
|
#if !BESTHTTP_DISABLE_WEBSOCKET |
|
this.PreferedTransport = TransportTypes.WebSocket; |
|
#else |
|
this.PreferedTransport = TransportTypes.LongPolling; |
|
#endif |
|
this.PingInterval = TimeSpan.FromSeconds(15); |
|
this.PingTimeoutInterval = TimeSpan.FromSeconds(30); |
|
this.MaxRedirects = 100; |
|
this.ConnectTimeout = TimeSpan.FromSeconds(60); |
|
} |
|
} |
|
|
|
public interface IRetryPolicy |
|
{ |
|
/// <summary> |
|
/// This function must return with a delay time to wait until a new connection attempt, or null to do not do another one. |
|
/// </summary> |
|
TimeSpan? GetNextRetryDelay(RetryContext context); |
|
} |
|
|
|
public struct RetryContext |
|
{ |
|
/// <summary> |
|
/// Previous reconnect attempts. A successful connection sets it back to zero. |
|
/// </summary> |
|
public uint PreviousRetryCount; |
|
|
|
/// <summary> |
|
/// Elapsed time since the original connection error. |
|
/// </summary> |
|
public TimeSpan ElapsedTime; |
|
|
|
/// <summary> |
|
/// String representation of the connection error. |
|
/// </summary> |
|
public string RetryReason; |
|
} |
|
|
|
public sealed class DefaultRetryPolicy : IRetryPolicy |
|
{ |
|
private static TimeSpan?[] DefaultBackoffTimes = new TimeSpan?[] |
|
{ |
|
TimeSpan.Zero, |
|
TimeSpan.FromSeconds(2), |
|
TimeSpan.FromSeconds(10), |
|
TimeSpan.FromSeconds(30), |
|
null |
|
}; |
|
|
|
TimeSpan?[] backoffTimes; |
|
|
|
public DefaultRetryPolicy() |
|
{ |
|
this.backoffTimes = DefaultBackoffTimes; |
|
} |
|
|
|
public DefaultRetryPolicy(TimeSpan?[] customBackoffTimes) |
|
{ |
|
this.backoffTimes = customBackoffTimes; |
|
} |
|
|
|
public TimeSpan? GetNextRetryDelay(RetryContext context) |
|
{ |
|
if (context.PreviousRetryCount >= this.backoffTimes.Length) |
|
return null; |
|
|
|
return this.backoffTimes[context.PreviousRetryCount]; |
|
} |
|
} |
|
} |
|
#endif
|
|
|