Ejemplo n.º 1
0
        public bool ShouldWeRenewToken(ErrorInfo error, RealtimeState state)
        {
            if (error == null)
            {
                return(false);
            }

            return(error.IsTokenError && state.AttemptsInfo.TriedToRenewToken == false && RestClient.AblyAuth.TokenRenewable);
        }
Ejemplo n.º 2
0
        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"); // >
        }
Ejemplo n.º 3
0
        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]);
            }
        }
Ejemplo n.º 4
0
        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);
        }
Ejemplo n.º 5
0
        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);
        }
Ejemplo n.º 6
0
        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));
        }
Ejemplo n.º 7
0
        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));
        }
Ejemplo n.º 8
0
        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();
            }
        }
Ejemplo n.º 9
0
        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);
        }
Ejemplo n.º 10
0
 public bool ShouldWeRenewToken(ErrorInfo error, RealtimeState state)
 {
     return(ShouldWeRenewTokenValue);
 }
Ejemplo n.º 11
0
 public virtual Task <bool> OnMessageReceived(ProtocolMessage message, RealtimeState state)
 {
     return(Task.FromResult(false));
 }
Ejemplo n.º 12
0
        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));
        }