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); }); }
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); }); }