Example #1
0
        private void InitializeFSM()
        {
            When(AssociationState.Closed, fsmEvent =>
            {
                State <AssociationState, ProtocolStateData> nextState = null;
                //Transport layer events for outbound associations
                fsmEvent.FsmEvent.Match()
                .With <Status.Failure>(f => fsmEvent.StateData.Match()
                                       .With <OutboundUnassociated>(ou =>
                {
                    ou.StatusCompletionSource.SetException(f.Cause);
                    nextState = Stop();
                }))
                .With <AssociationHandle>(h => fsmEvent.StateData.Match()
                                          .With <OutboundUnassociated>(ou =>
                {
                    var wrappedHandle = h;
                    var statusPromise = ou.StatusCompletionSource;
                    wrappedHandle.ReadHandlerSource.SetResult(new ActorHandleEventListener(Self));
                    if (SendAssociate(wrappedHandle, _localHandshakeInfo))
                    {
                        _failureDetector.HeartBeat();
                        InitTimers();
                        nextState =
                            GoTo(AssociationState.WaitHandshake)
                            .Using(new OutboundUnderlyingAssociated(statusPromise, wrappedHandle));
                    }
                    else
                    {
                        SetTimer("associate-retry", wrappedHandle,
                                 Context.System.Provider.AsInstanceOf <RemoteActorRefProvider>()
                                 .RemoteSettings.BackoffPeriod, repeat: false);
                        nextState = Stay();
                    }
                }))
                .With <DisassociateUnderlying>(d =>
                {
                    nextState = Stop();
                })
                .Default(m => { nextState = Stay(); });

                return(nextState);
            });

            //Transport layer events for outbound associations
            When(AssociationState.WaitHandshake, @event =>
            {
                State <AssociationState, ProtocolStateData> nextState = null;

                @event.FsmEvent.Match()
                .With <UnderlyingTransportError>(e =>
                {
                    PublishError(e);
                    nextState = Stay();
                })
                .With <Disassociated>(d =>
                {
                    nextState = Stop(new Failure(d.Info));
                })
                .With <InboundPayload>(m =>
                {
                    var pdu = DecodePdu(m.Payload);
                    @event.StateData.Match()
                    .With <OutboundUnderlyingAssociated>(ola =>
                    {
                        var wrappedHandle          = ola.WrappedHandle;
                        var statusCompletionSource = ola.StatusCompletionSource;
                        pdu.Match()
                        .With <Associate>(a =>
                        {
                            var handshakeInfo = a.Info;
                            if (_refuseUid.HasValue && _refuseUid == handshakeInfo.Uid)             //refused UID
                            {
                                SendDisassociate(wrappedHandle, DisassociateInfo.Quarantined);
                                nextState = Stop(new Failure(new ForbiddenUidReason()));
                            }
                            else             //accepted UID
                            {
                                _failureDetector.HeartBeat();
                                nextState =
                                    GoTo(AssociationState.Open)
                                    .Using(
                                        new AssociatedWaitHandler(
                                            NotifyOutboundHandler(wrappedHandle, handshakeInfo,
                                                                  statusCompletionSource), wrappedHandle,
                                            new Queue <ByteString>()));
                            }
                        })
                        .With <Disassociate>(d =>
                        {
                            //After receiving Disassociate we MUST NOT send back a Disassociate (loop)
                            nextState = Stop(new Failure(d.Reason));
                        })
                        .Default(d =>
                        {
                            _log.Debug(string.Format("Exepcted message of type Associate; instead received {0}", d));
                            //Expect handshake to be finished, dropping connection
                            SendDisassociate(wrappedHandle, DisassociateInfo.Unknown);
                            nextState = Stop();
                        });
                    })
                    .With <InboundUnassociated>(iu =>
                    {
                        var associationHandler = iu.AssociationEventListener;
                        var wrappedHandle      = iu.WrappedHandle;
                        pdu.Match()
                        .With <Disassociate>(d => nextState = Stop(new Failure(d.Reason)))
                        .With <Associate>(a =>
                        {
                            SendAssociate(wrappedHandle, _localHandshakeInfo);
                            _failureDetector.HeartBeat();
                            InitTimers();
                            nextState =
                                GoTo(AssociationState.Open)
                                .Using(
                                    new AssociatedWaitHandler(
                                        NotifyInboundHandler(wrappedHandle, a.Info, associationHandler),
                                        wrappedHandle, new Queue <ByteString>()));
                        })
                        .Default(d =>
                        {
                            SendDisassociate(wrappedHandle, DisassociateInfo.Unknown);
                            nextState = Stop();
                        });
                    });
                })
                .With <HeartbeatTimer>(h => @event.StateData.Match()
                                       .With <OutboundUnderlyingAssociated>(ou => nextState = HandleTimers(ou.WrappedHandle)));

                return(nextState);
            });

            When(AssociationState.Open, @event =>
            {
                State <AssociationState, ProtocolStateData> nextState = null;
                @event.FsmEvent.Match()
                .With <UnderlyingTransportError>(e =>
                {
                    PublishError(e);
                    nextState = Stay();
                })
                .With <Disassociated>(d =>
                {
                    nextState = Stop(new Failure(d.Info));
                })
                .With <InboundPayload>(ip =>
                {
                    var pdu = DecodePdu(ip.Payload);
                    pdu.Match()
                    .With <Disassociate>(d => nextState = Stop(new Failure(d.Reason)))
                    .With <Heartbeat>(h =>
                    {
                        _failureDetector.HeartBeat();
                        nextState = Stay();
                    })
                    .With <Payload>(p => @event.StateData.Match()
                                    .With <AssociatedWaitHandler>(awh =>
                    {
                        var nQueue = new Queue <ByteString>(awh.Queue);
                        nQueue.Enqueue(p.Bytes);
                        nextState =
                            Stay()
                            .Using(new AssociatedWaitHandler(awh.HandlerListener, awh.WrappedHandle,
                                                             nQueue));
                    })
                                    .With <ListenerReady>(lr =>
                    {
                        lr.Listener.Notify(new InboundPayload(p.Bytes));
                        nextState = Stay();
                    })
                                    .Default(msg =>
                    {
                        throw new AkkaProtocolException(
                            string.Format("Unhandled message in state Open(InboundPayload) with type {0}",
                                          msg));
                    }))
                    .Default(d =>
                    {
                        nextState = Stay();
                    });
                })
                .With <HeartbeatTimer>(hrt => @event.StateData.Match()
                                       .With <AssociatedWaitHandler>(awh => nextState = HandleTimers(awh.WrappedHandle))
                                       .With <ListenerReady>(lr => nextState          = HandleTimers(lr.WrappedHandle)))
                .With <DisassociateUnderlying>(du =>
                {
                    AssociationHandle handle = null;
                    @event.StateData.Match()
                    .With <ListenerReady>(lr => handle          = lr.WrappedHandle)
                    .With <AssociatedWaitHandler>(awh => handle = awh.WrappedHandle)
                    .Default(
                        msg =>
                    {
                        throw new AkkaProtocolException(
                            string.Format(
                                "unhandled message in state Open(DisassociateUnderlying) with type {0}", msg));
                    });
                    SendDisassociate(handle, du.Info);
                    nextState = Stop();
                })
                .With <HandleListenerRegistered>(hlr => @event.StateData.Match()
                                                 .With <AssociatedWaitHandler>(awh =>
                {
                    foreach (var msg in awh.Queue)
                    {
                        hlr.Listener.Notify(new InboundPayload(msg));
                    }
                    nextState = Stay().Using(new ListenerReady(hlr.Listener, awh.WrappedHandle));
                }));

                return(nextState);
            });

            OnTermination(@event => @event.StateData.Match()
                          .With <OutboundUnassociated>(ou => ou.StatusCompletionSource.TrySetException(@event.Reason is Failure
                    ? new AkkaProtocolException(@event.Reason.ToString())
                    : new AkkaProtocolException("Transport disassociated before handshake finished")))
                          .With <OutboundUnderlyingAssociated>(oua =>
            {
                Exception associationFailure = null;
                @event.Reason.Match()
                .With <Failure>(f => f.Cause.Match()
                                .With <TimeoutReason>(
                                    timeout =>
                                    associationFailure =
                                        new AkkaProtocolException("No reponse from remote. Handshake timed out."))
                                .With <ForbiddenUidReason>(
                                    forbidden =>
                                    associationFailure =
                                        new AkkaProtocolException(
                                            "The remote system has a UID that has been quarantined. Association aborted."))
                                .With <DisassociateInfo>(info => associationFailure = DisassociateException(info))
                                .Default(
                                    msg =>
                                    associationFailure =
                                        new AkkaProtocolException(
                                            "Transport disassociated before handshake finished")));

                oua.StatusCompletionSource.TrySetException(associationFailure);
                oua.WrappedHandle.Disassociate();
            })
                          .With <AssociatedWaitHandler>(awh =>
            {
                Disassociated disassociateNotification = null;
                if (@event.Reason is Failure && ((Failure)@event.Reason).Cause is DisassociateInfo)
                {
                    disassociateNotification =
                        new Disassociated(((Failure)@event.Reason).Cause.AsInstanceOf <DisassociateInfo>());
                }
                else
                {
                    disassociateNotification = new Disassociated(DisassociateInfo.Unknown);
                }
                awh.HandlerListener.ContinueWith(result => result.Result.Notify(disassociateNotification),
                                                 TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.AttachedToParent);
            })
                          .With <ListenerReady>(lr =>
            {
                Disassociated disassociateNotification = null;
                if (@event.Reason is Failure && ((Failure)@event.Reason).Cause is DisassociateInfo)
                {
                    disassociateNotification =
                        new Disassociated(((Failure)@event.Reason).Cause.AsInstanceOf <DisassociateInfo>());
                }
                else
                {
                    disassociateNotification = new Disassociated(DisassociateInfo.Unknown);
                }
                lr.Listener.Notify(disassociateNotification);
                lr.WrappedHandle.Disassociate();
            })
                          .With <InboundUnassociated>(iu =>
                                                      iu.WrappedHandle.Disassociate()));

            _initialData.Match()
            .With <OutboundUnassociated>(d =>
            {
                d.Transport.Associate(d.RemoteAddress).PipeTo(Self);
                StartWith(AssociationState.Closed, d);
            })
            .With <InboundUnassociated>(d =>
            {
                d.WrappedHandle.ReadHandlerSource.SetResult(new ActorHandleEventListener(Self));
                StartWith(AssociationState.WaitHandshake, d);
            });
        }
Example #2
0
        private void InitializeFSM()
        {
            When(AssociationState.Closed, fsmEvent =>
            {
                State <AssociationState, ProtocolStateData> nextState = null;
                //Transport layer events for outbound associations
                fsmEvent.FsmEvent.Match()
                .With <Status.Failure>(f => fsmEvent.StateData.Match()
                                       .With <OutboundUnassociated>(ou =>
                {
                    ou.StatusCompletionSource.SetException(f.Cause);
                    nextState = Stop();
                }))
                .With <AssociationHandle>(h => fsmEvent.StateData.Match()
                                          .With <OutboundUnassociated>(ou =>
                {
                    /*
                     * Association has been established, but handshake is not yet complete.
                     * This actor, the outbound ProtocolStateActor, can now set itself as
                     * the read handler for the remainder of the handshake process.
                     */
                    AssociationHandle wrappedHandle = h;
                    var statusPromise = ou.StatusCompletionSource;
                    wrappedHandle.ReadHandlerSource.TrySetResult(new ActorHandleEventListener(Self));
                    if (SendAssociate(wrappedHandle, _localHandshakeInfo))
                    {
                        _failureDetector.HeartBeat();
                        InitTimers();
                        // wait for reply from the inbound side of the connection (WaitHandshake)
                        nextState =
                            GoTo(AssociationState.WaitHandshake)
                            .Using(new OutboundUnderlyingAssociated(statusPromise, wrappedHandle));
                    }
                    else
                    {
                        //Otherwise, retry
                        SetTimer("associate-retry", wrappedHandle,
                                 ((RemoteActorRefProvider)((ActorSystemImpl)Context.System).Provider)      //TODO: rewrite using RARP ActorSystem Extension
                                 .RemoteSettings.BackoffPeriod, repeat: false);
                        nextState = Stay();
                    }
                }))
                .With <DisassociateUnderlying>(d =>
                {
                    nextState = Stop();
                })
                .Default(m => { nextState = Stay(); });

                return(nextState);
            });

            //Transport layer events for outbound associations
            When(AssociationState.WaitHandshake, @event =>
            {
                State <AssociationState, ProtocolStateData> nextState = null;

                @event.FsmEvent.Match()
                .With <UnderlyingTransportError>(e =>
                {
                    PublishError(e);
                    nextState = Stay();
                })
                .With <Disassociated>(d =>
                {
                    nextState = Stop(new Failure(d.Info));
                })
                .With <InboundPayload>(m =>
                {
                    var pdu = DecodePdu(m.Payload);
                    @event.StateData.Match()
                    .With <OutboundUnderlyingAssociated>(ola =>
                    {
                        /*
                         * This state is used for OutboundProtocolState actors when they receive
                         * a reply back from the inbound end of the association.
                         */
                        var wrappedHandle          = ola.WrappedHandle;
                        var statusCompletionSource = ola.StatusCompletionSource;
                        pdu.Match()
                        .With <Associate>(a =>
                        {
                            var handshakeInfo = a.Info;
                            if (_refuseUid.HasValue && _refuseUid == handshakeInfo.Uid)             //refused UID
                            {
                                SendDisassociate(wrappedHandle, DisassociateInfo.Quarantined);
                                nextState = Stop(new Failure(new ForbiddenUidReason()));
                            }
                            else             //accepted UID
                            {
                                _failureDetector.HeartBeat();
                                nextState =
                                    GoTo(AssociationState.Open)
                                    .Using(
                                        new AssociatedWaitHandler(
                                            NotifyOutboundHandler(wrappedHandle, handshakeInfo,
                                                                  statusCompletionSource), wrappedHandle,
                                            new Queue <ByteString>()));
                            }
                        })
                        .With <Disassociate>(d =>
                        {
                            //After receiving Disassociate we MUST NOT send back a Disassociate (loop)
                            nextState = Stop(new Failure(d.Reason));
                        })
                        .Default(d =>
                        {
                            _log.Debug(string.Format("Expected message of type Associate; instead received {0}", d));
                            //Expect handshake to be finished, dropping connection
                            SendDisassociate(wrappedHandle, DisassociateInfo.Unknown);
                            nextState = Stop();
                        });
                    })
                    .With <InboundUnassociated>(iu =>
                    {
                        /*
                         * This state is used by inbound protocol state actors
                         * when they receive an association attempt from the
                         * outbound side of the association.
                         */
                        var associationHandler = iu.AssociationEventListener;
                        var wrappedHandle      = iu.WrappedHandle;
                        pdu.Match()
                        .With <Disassociate>(d =>
                        {
                            nextState = Stop(new Failure(d.Reason));
                        })
                        .With <Associate>(a =>
                        {
                            SendAssociate(wrappedHandle, _localHandshakeInfo);
                            _failureDetector.HeartBeat();
                            InitTimers();
                            nextState =
                                GoTo(AssociationState.Open)
                                .Using(
                                    new AssociatedWaitHandler(
                                        NotifyInboundHandler(wrappedHandle, a.Info, associationHandler),
                                        wrappedHandle, new Queue <ByteString>()));
                        })
                        .Default(d =>
                        {
                            SendDisassociate(wrappedHandle, DisassociateInfo.Unknown);
                            nextState = Stop();
                        });
                    });
                })
                .With <HeartbeatTimer>(h => @event.StateData.Match()
                                       .With <OutboundUnderlyingAssociated>(ou => nextState = HandleTimers(ou.WrappedHandle)));

                return(nextState);
            });

            When(AssociationState.Open, @event =>
            {
                State <AssociationState, ProtocolStateData> nextState = null;
                @event.FsmEvent.Match()
                .With <UnderlyingTransportError>(e =>
                {
                    PublishError(e);
                    nextState = Stay();
                })
                .With <Disassociated>(d =>
                {
                    nextState = Stop(new Failure(d.Info));
                })
                .With <InboundPayload>(ip =>
                {
                    var pdu = DecodePdu(ip.Payload);
                    pdu.Match()
                    .With <Disassociate>(d =>
                    {
                        nextState = Stop(new Failure(d.Reason));
                    })
                    .With <Heartbeat>(h =>
                    {
                        _failureDetector.HeartBeat();
                        nextState = Stay();
                    })
                    .With <Payload>(p =>
                    {
                        _failureDetector.HeartBeat();
                        @event.StateData.Match()
                        .With <AssociatedWaitHandler>(awh =>
                        {
                            var nQueue = new Queue <ByteString>(awh.Queue);
                            nQueue.Enqueue(p.Bytes);
                            nextState =
                                Stay()
                                .Using(new AssociatedWaitHandler(awh.HandlerListener, awh.WrappedHandle,
                                                                 nQueue));
                        })
                        .With <ListenerReady>(lr =>
                        {
                            lr.Listener.Notify(new InboundPayload(p.Bytes));
                            nextState = Stay();
                        })
                        .Default(msg =>
                        {
                            throw new AkkaProtocolException(
                                string.Format(
                                    "Unhandled message in state Open(InboundPayload) with type {0}",
                                    msg));
                        });
                    })
                    .Default(d =>
                    {
                        nextState = Stay();
                    });
                })
                .With <HeartbeatTimer>(hrt => @event.StateData.Match()
                                       .With <AssociatedWaitHandler>(awh => nextState = HandleTimers(awh.WrappedHandle))
                                       .With <ListenerReady>(lr => nextState          = HandleTimers(lr.WrappedHandle)))
                .With <DisassociateUnderlying>(du =>
                {
                    AssociationHandle handle = null;
                    @event.StateData.Match()
                    .With <ListenerReady>(lr => handle          = lr.WrappedHandle)
                    .With <AssociatedWaitHandler>(awh => handle = awh.WrappedHandle)
                    .Default(
                        msg =>
                    {
                        throw new AkkaProtocolException(
                            string.Format(
                                "unhandled message in state Open(DisassociateUnderlying) with type {0}", msg));
                    });
                    SendDisassociate(handle, du.Info);
                    nextState = Stop();
                })
                .With <HandleListenerRegistered>(hlr => @event.StateData.Match()
                                                 .With <AssociatedWaitHandler>(awh =>
                {
                    foreach (var msg in awh.Queue)
                    {
                        hlr.Listener.Notify(new InboundPayload(msg));
                    }
                    nextState = Stay().Using(new ListenerReady(hlr.Listener, awh.WrappedHandle));
                }));

                return(nextState);
            });

            OnTermination(@event => @event.StateData.Match()
                          .With <OutboundUnassociated>(ou => ou.StatusCompletionSource.TrySetException(@event.Reason is Failure
                    ? new AkkaProtocolException(@event.Reason.ToString())
                    : new AkkaProtocolException("Transport disassociated before handshake finished")))
                          .With <OutboundUnderlyingAssociated>(oua =>
            {
                Exception associationFailure = null;
                @event.Reason.Match()
                .With <Failure>(f => f.Cause.Match()
                                .With <TimeoutReason>(
                                    timeout =>
                                    associationFailure =
                                        new AkkaProtocolException(timeout.ErrorMessage))
                                .With <ForbiddenUidReason>(
                                    forbidden =>
                                    associationFailure =
                                        new AkkaProtocolException(
                                            "The remote system has a UID that has been quarantined. Association aborted."))
                                .With <DisassociateInfo>(info => associationFailure = DisassociateException(info)))
                .Default(
                    msg =>
                    associationFailure =
                        new AkkaProtocolException(
                            "Transport disassociated before handshake finished"));

                oua.StatusCompletionSource.TrySetException(associationFailure);
                oua.WrappedHandle.Disassociate();
            })
                          .With <AssociatedWaitHandler>(awh =>
            {
                Disassociated disassociateNotification = null;
                if (@event.Reason is Failure && @event.Reason.AsInstanceOf <Failure>().Cause is DisassociateInfo)
                {
                    disassociateNotification =
                        new Disassociated(@event.Reason.AsInstanceOf <Failure>().Cause.AsInstanceOf <DisassociateInfo>());
                }
                else
                {
                    disassociateNotification = new Disassociated(DisassociateInfo.Unknown);
                }
                awh.HandlerListener.ContinueWith(result => result.Result.Notify(disassociateNotification),
                                                 TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.AttachedToParent);
            })
                          .With <ListenerReady>(lr =>
            {
                Disassociated disassociateNotification = null;
                if (@event.Reason is Failure && ((Failure)@event.Reason).Cause is DisassociateInfo)
                {
                    disassociateNotification =
                        new Disassociated(((Failure)@event.Reason).Cause.AsInstanceOf <DisassociateInfo>());
                }
                else
                {
                    disassociateNotification = new Disassociated(DisassociateInfo.Unknown);
                }
                lr.Listener.Notify(disassociateNotification);
                lr.WrappedHandle.Disassociate();
            })
                          .With <InboundUnassociated>(iu =>
                                                      iu.WrappedHandle.Disassociate()));

            /*
             * Set the initial ProtocolStateActor state to CLOSED if OUTBOUND
             * Set the initial ProtocolStateActor state to WAITHANDSHAKE if INBOUND
             * */
            _initialData.Match()
            .With <OutboundUnassociated>(d =>
            {
                // attempt to open underlying transport to the remote address
                // if using Helios, this is where the socket connection is opened.
                d.Transport.Associate(d.RemoteAddress).PipeTo(Self);
                StartWith(AssociationState.Closed, d);
            })
            .With <InboundUnassociated>(d =>
            {
                // inbound transport is opened already inside the ProtocolStateManager
                // therefore we just have to set ourselves as listener and wait for
                // incoming handshake attempts from the client.
                d.WrappedHandle.ReadHandlerSource.SetResult(new ActorHandleEventListener(Self));
                StartWith(AssociationState.WaitHandshake, d);
            });
        }