public bool ShouldWeRenewToken(ErrorInfo error, RealtimeState state) { if (error == null) { return(false); } return(error.IsTokenError && state.AttemptsInfo.TriedToRenewToken == false && RestClient.AblyAuth.TokenRenewable); }
public void ShouldSuspend_WhenFirstAttemptEqualOrGreaterThanConnectionStateTtl_ShouldReturnTrue() { var now = new Now(); var state = new RealtimeState(null, now.ValueFn); state.AttemptsInfo.Attempts.Add(new ConnectionAttempt(now.Value)); // Move now to default ConnectionStateTtl - 1 second now.Reset(DateTimeOffset.UtcNow.Add(Defaults.ConnectionStateTtl)); state.ShouldSuspend(now.ValueFn).Should().BeTrue("When time is equal"); // = now.Reset(DateTimeOffset.UtcNow.Add(Defaults.ConnectionStateTtl).AddSeconds(60)); state.ShouldSuspend(now.ValueFn).Should().BeTrue("When time is greater than"); // > }
public static string GetHost(RealtimeState state, string realtimeHost) { var lastFailedState = state.AttemptsInfo.Attempts.SelectMany(x => x.FailedStates).LastOrDefault(x => x.ShouldUseFallback()); string customHost = string.Empty; var disconnectedCount = state.AttemptsInfo.DisconnectedCount(); var suspendedCount = state.AttemptsInfo.SuspendedCount(); if (lastFailedState != null) { // RTN17a if we were previously connected to a fallback host // we need to first try again on the default host before starting to check fallback hosts if (state.Connection.Host.IsNotEmpty() && state.Connection.IsFallbackHost) { return(realtimeHost); } if (lastFailedState.State == ConnectionState.Disconnected) { // DisconnectedCount will always be > 0 and we want to start with the first host. customHost = GetFallbackHost(disconnectedCount); } if (lastFailedState.State == ConnectionState.Suspended && suspendedCount > 1) { customHost = GetFallbackHost(disconnectedCount + suspendedCount); } if (customHost.IsNotEmpty()) { return(customHost); } } return(realtimeHost); string GetFallbackHost(int failedRequestCount) { if (state.Connection.FallbackHosts.Count == 0) { return(string.Empty); } // We -1 because the index is 0 based where the failedRequestCount starts from 1 var index = (failedRequestCount - 1) % state.Connection.FallbackHosts.Count; return(state.Connection.FallbackHosts[index]); } }
public override async Task <bool> OnMessageReceived(ProtocolMessage message, RealtimeState state) { switch (message.Action) { case ProtocolMessage.MessageAction.Auth: Context.ExecuteCommand(RetryAuthCommand.Create(false).TriggeredBy("ConnectedState.OnMessageReceived()")); return(true); case ProtocolMessage.MessageAction.Connected: Context.ExecuteCommand(SetConnectedStateCommand.Create(message, isUpdate: true).TriggeredBy("ConnectedState.OnMessageReceived()")); return(true); case ProtocolMessage.MessageAction.Close: Context.ExecuteCommand(SetClosedStateCommand.Create(message.Error).TriggeredBy("ConnectedState.OnMessageReceived()")); return(true); case ProtocolMessage.MessageAction.Disconnected: if (message.Error?.IsTokenError ?? false) { if (Context.ShouldWeRenewToken(message.Error, state)) { Context.ExecuteCommand(RetryAuthCommand.Create(message.Error, true).TriggeredBy("ConnectedState.OnMessageReceived()")); } else { Context.ExecuteCommand(SetFailedStateCommand.Create(message.Error).TriggeredBy("ConnectedState.OnMessageReceived()")); } return(true); } Context.ExecuteCommand(SetDisconnectedStateCommand.Create(message.Error).TriggeredBy("ConnectedState.OnMessageReceived()")); return(true); case ProtocolMessage.MessageAction.Error: // an error message may signify an error state in the connection or in a channel // Only handle connection errors here. if (message.Channel.IsEmpty()) { Context.ExecuteCommand(SetFailedStateCommand.Create(message.Error).TriggeredBy("ConnectedState.OnMessageReceived()")); } return(true); } return(false); }
public static bool ShouldSuspend(this RealtimeState state, Func <DateTimeOffset> now = null) { var firstAttempt = state.AttemptsInfo.FirstAttempt; if (firstAttempt == null) { return(false); } if (state.AttemptsInfo.SuspendedCount() > 0) { return(true); } now = now ?? Defaults.NowFunc(); return((now() - firstAttempt.Value) >= state.Connection.ConnectionStateTtl); }
public Task <bool> OnMessageReceived(ProtocolMessage message, RealtimeState state) { var canHandle = CanHandleMessage(message); if (canHandle && message.Id.IsNotEmpty()) { var pingRequest = state.PingRequests.FirstOrDefault(x => x.Id.EqualsTo(message.Id)); if (pingRequest != null) { state.PingRequests.Remove(pingRequest); TryCallback(pingRequest.Callback, GetElapsedTime(pingRequest), null); } } return(Task.FromResult(canHandle)); }
public override Task <bool> OnMessageReceived(ProtocolMessage message, RealtimeState state) { switch (message.Action) { case ProtocolMessage.MessageAction.Closed: TransitionState(SetClosedStateCommand.Create().TriggeredBy("ClosingState.OnMessageReceived()")); return(Task.FromResult(true)); case ProtocolMessage.MessageAction.Disconnected: TransitionState(SetDisconnectedStateCommand.Create(message.Error).TriggeredBy("ClosingState.OnMessageReceived()")); return(Task.FromResult(true)); case ProtocolMessage.MessageAction.Error: TransitionState(SetFailedStateCommand.Create(message.Error).TriggeredBy("ClosingState.OnMessageReceived()")); return(Task.FromResult(true)); } return(Task.FromResult(false)); }
internal AblyRealtime(ClientOptions options, Func <ClientOptions, IMobileDevice, AblyRest> createRestFunc, IMobileDevice mobileDevice = null) { if (options.Logger != null) { Logger = options.Logger; } Logger.LogLevel = options.LogLevel; if (options.LogHandler != null) { Logger.LoggerSink = options.LogHandler; } CaptureSynchronizationContext(options); RestClient = createRestFunc != null?createRestFunc.Invoke(options, mobileDevice) : new AblyRest(options, mobileDevice); Push = new PushRealtime(RestClient, Logger); Connection = new Connection(this, options.NowFunc, options.Logger); Connection.Initialise(); if (options.AutomaticNetworkStateMonitoring) { IoC.RegisterOsNetworkStateChanged(); } Channels = new RealtimeChannels(this, Connection, mobileDevice); RestClient.AblyAuth.OnAuthUpdated = ConnectionManager.OnAuthUpdated; State = new RealtimeState(options.GetFallbackHosts()?.Shuffle().ToList(), options.NowFunc); Workflow = new RealtimeWorkflow(this, Logger); Workflow.Start(); if (options.AutoConnect) { Connect(); } }
public override async Task <bool> OnMessageReceived(ProtocolMessage message, RealtimeState state) { if (message == null) { throw new ArgumentNullException(nameof(message), "Null message passed to Connection Connecting State"); } switch (message.Action) { case ProtocolMessage.MessageAction.Connected: { if (Context.Transport.State == TransportState.Connected) { TransitionState(SetConnectedStateCommand.Create(message, false) .TriggeredBy("ConnectingState.OnMessageReceived(Connected)")); } return(true); } case ProtocolMessage.MessageAction.Disconnected: { Context.ExecuteCommand(HandleConnectingDisconnectedCommand.Create(message.Error) .TriggeredBy("ConnectingState.OnMessageReceived(Disconnected)")); return(true); } case ProtocolMessage.MessageAction.Error: { Context.ExecuteCommand(HandleConnectingErrorCommand.Create(message.Error) .TriggeredBy("ConnectingState.OnMessageReceived(Error)")); return(true); } } return(false); }
public bool ShouldWeRenewToken(ErrorInfo error, RealtimeState state) { return(ShouldWeRenewTokenValue); }
public virtual Task <bool> OnMessageReceived(ProtocolMessage message, RealtimeState state) { return(Task.FromResult(false)); }
public Task <bool> MessageReceived(ProtocolMessage protocolMessage, RealtimeState state) { if (protocolMessage.Channel.IsEmpty()) { return(Task.FromResult(false)); } var channel = _channels.Exists(protocolMessage.Channel) ? GetChannel(protocolMessage.Channel) : null; if (channel == null) { Logger.Warning($"Message received {protocolMessage} for a channel that does not exist {protocolMessage.Channel}"); return(Task.FromResult(false)); } switch (protocolMessage.Action) { case ProtocolMessage.MessageAction.Error: channel.SetChannelState(ChannelState.Failed, protocolMessage); break; case ProtocolMessage.MessageAction.Attached: channel.Properties.AttachSerial = protocolMessage.ChannelSerial; if (protocolMessage.Flags.HasValue) { var modes = new ChannelModes(((ProtocolMessage.Flag)protocolMessage.Flags.Value).FromFlag()); channel.Modes = new ReadOnlyChannelModes(modes.ToList()); } if (protocolMessage.Params != null) { channel.Params = new ReadOnlyChannelParams(protocolMessage.Params); } if (channel.State == ChannelState.Attached) { // RTL12 if (!protocolMessage.HasFlag(ProtocolMessage.Flag.Resumed)) { channel.Presence.ChannelAttached(protocolMessage); channel.EmitUpdate(protocolMessage.Error, false, protocolMessage); } } else { channel.SetChannelState(ChannelState.Attached, protocolMessage); } break; case ProtocolMessage.MessageAction.Detach: case ProtocolMessage.MessageAction.Detached: if (channel.State != ChannelState.Detached) { channel.SetChannelState(ChannelState.Detached, protocolMessage); } break; case ProtocolMessage.MessageAction.Message: if (channel.State == ChannelState.Attaching) { Logger.Warning( $"Channel #{channel.Name} is currently in Attaching state. Messages received in this state are ignored. Ignoring ${protocolMessage.Messages?.Length ?? 0} messages"); return(TaskConstants.BooleanTrue); } if (ValidateIfDeltaItHasCorrectPreviousMessageId(protocolMessage, channel.LastSuccessfulMessageIds) == false) { var message = "Delta message decode failure. Previous message id does not equal expected message id."; var reason = new ErrorInfo(message, ErrorCodes.VcDiffDecodeError); channel.StartDecodeFailureRecovery(reason); return(TaskConstants.BooleanTrue); } var result = _messageHandler.DecodeMessages( protocolMessage, protocolMessage.Messages, channel.MessageDecodingContext); if (result.IsFailure) { Logger.Error($"{channel.Name} - failed to decode message. ErrorCode: {result.Error.Code}, Message: {result.Error.Message}"); if (result.Error is VcDiffErrorInfo) { channel.StartDecodeFailureRecovery(result.Error); // Break any further message processing return(TaskConstants.BooleanTrue); } } channel.LastSuccessfulMessageIds = LastMessageIds.Create(protocolMessage); foreach (var msg in protocolMessage.Messages) { channel.OnMessage(msg); } break; case ProtocolMessage.MessageAction.Presence: case ProtocolMessage.MessageAction.Sync: var presenceDecodeResult = _messageHandler.DecodeMessages( protocolMessage, protocolMessage.Presence, channel.Options); if (presenceDecodeResult.IsFailure) { Logger.Error($"{channel.Name} - failed to decode presence message. ErrorCode: {presenceDecodeResult.Error.Code}, Message: {presenceDecodeResult.Error.Message}"); channel.OnError(presenceDecodeResult.Error); } string syncSerial = protocolMessage.Action == ProtocolMessage.MessageAction.Sync ? protocolMessage.ChannelSerial : null; channel.Presence.OnPresence(protocolMessage.Presence, syncSerial); break; } return(Task.FromResult(true)); }