Unity_Web/Assets/Best HTTP/Source/SignalRCore/HelperClasses.cs

298 lines
8.4 KiB
C#

#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;
}
}
struct FunctionCallbackDescriptor
{
public readonly Type ReturnType;
public readonly Type[] ParamTypes;
public readonly Func<object[], object> Callback;
public FunctionCallbackDescriptor(Type returnType, Type[] paramTypes, Func<object[], object> callback)
{
this.ReturnType = returnType;
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 List<FunctionCallbackDescriptor> functionCallbacks;
public void Add(Type[] paramTypes, Action<object[]> callback)
{
this.callbacks.Add(new CallbackDescriptor(paramTypes, callback));
}
public void AddFunc(Type resultType, Type[] paramTypes, Func<object[], object> callback)
{
if (this.functionCallbacks == null)
this.functionCallbacks = new List<FunctionCallbackDescriptor>(1);
this.functionCallbacks.Add(new FunctionCallbackDescriptor(resultType, paramTypes, callback));
}
}
public sealed class WebsocketOptions
{
#if !BESTHTTP_DISABLE_WEBSOCKET && (!UNITY_WEBGL || UNITY_EDITOR)
public Func<WebSocket.Extensions.IExtension[]> ExtensionsFactory { get; set; } = WebSocket.WebSocket.GetDefaultExtensions;
public TimeSpan? PingIntervalOverride { get; set; } = TimeSpan.Zero;
#endif
}
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; }
/// <summary>
/// Customization options for the websocket transport.
/// </summary>
public WebsocketOptions WebsocketOptions { get; set; } = new WebsocketOptions();
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