/// <summary> /// Message-processing behavior when the <see cref="EndpointManager"/> is able to accept /// inbound association requests. /// </summary> protected void Accepting() { Receive <ManagementCommand>(mc => { /* * applies a management command to all available transports. * * Useful for things like global restart */ var sender = Sender; var allStatuses = _transportMapping.Values.Select(x => x.ManagementCommand(mc.Cmd)); Task.WhenAll(allStatuses) .ContinueWith(x => { return(new ManagementCommandAck(x.Result.All(y => y))); }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default) .PipeTo(sender); }); Receive <Quarantine>(quarantine => { //Stop writers var policy = Tuple.Create(_endpoints.WritableEndpointWithPolicyFor(quarantine.RemoteAddress), quarantine.Uid); if (policy.Item1 is Pass && policy.Item2 == null) { var endpoint = policy.Item1.AsInstanceOf <Pass>().Endpoint; Context.Stop(endpoint); _log.Warning("Association to [{0}] with unknown UID is reported as quarantined, but " + "address cannot be quarantined without knowing the UID, gating instead for {1} ms.", quarantine.RemoteAddress, _settings.RetryGateClosedFor.TotalMilliseconds); _endpoints.MarkAsFailed(endpoint, Deadline.Now + _settings.RetryGateClosedFor); } else if (policy.Item1 is Pass && policy.Item2 != null) { var pass = (Pass)policy.Item1; var uidOption = pass.Uid; var quarantineUid = policy.Item2; if (uidOption == quarantineUid) { _endpoints.MarkAsQuarantined(quarantine.RemoteAddress, quarantineUid.Value, Deadline.Now + _settings.QuarantineDuration); _eventPublisher.NotifyListeners(new QuarantinedEvent(quarantine.RemoteAddress, quarantineUid.Value)); Context.Stop(pass.Endpoint); } // or it does not match with the UID to be quarantined else if (!uidOption.HasValue && pass.RefuseUid != quarantineUid) { // the quarantine uid may be got fresh by cluster gossip, so update refuseUid for late handle when the writer got uid _endpoints.RegisterWritableEndpointRefuseUid(quarantine.RemoteAddress, quarantineUid.Value); } else { //the quarantine uid has lost the race with some failure, do nothing } } else if (policy.Item1 is WasGated && policy.Item2 != null) { var wg = (WasGated)policy.Item1; if (wg.RefuseUid == policy.Item2) { _endpoints.RegisterWritableEndpointRefuseUid(quarantine.RemoteAddress, policy.Item2.Value); } } else if (policy.Item1 is Quarantined && policy.Item2 != null && policy.Item1.AsInstanceOf <Quarantined>().Uid == policy.Item2.Value) { // the UID to be quarantined already exists, do nothing } else if (policy.Item2 != null) { // the current state is gated or quarantined, and we know the UID, update _endpoints.MarkAsQuarantined(quarantine.RemoteAddress, policy.Item2.Value, Deadline.Now + _settings.QuarantineDuration); _eventPublisher.NotifyListeners(new QuarantinedEvent(quarantine.RemoteAddress, policy.Item2.Value)); } else { // the current state is Gated, WasGated, or Quarantined and we don't know the UID, do nothing. } // Stop inbound read-only associations var readPolicy = Tuple.Create(_endpoints.ReadOnlyEndpointFor(quarantine.RemoteAddress), quarantine.Uid); if (readPolicy.Item1?.Item1 != null && quarantine.Uid == null) { Context.Stop(readPolicy.Item1.Item1); } else if (readPolicy.Item1?.Item1 != null && quarantine.Uid != null && readPolicy.Item1?.Item2 == quarantine.Uid) { Context.Stop(readPolicy.Item1.Item1); } else { } // nothing to stop Func <AkkaProtocolHandle, bool> matchesQuarantine = handle => handle.RemoteAddress.Equals(quarantine.RemoteAddress) && quarantine.Uid == handle.HandshakeInfo.Uid; // Stop all matching pending read handoffs _pendingReadHandoffs = _pendingReadHandoffs.Where(x => { var drop = matchesQuarantine(x.Value); // Side-effecting here if (drop) { x.Value.Disassociate(); Context.Stop(x.Key); } return(!drop); }).ToDictionary(key => key.Key, value => value.Value); // Stop all matching stashed connections _stashedInbound = _stashedInbound.Select(x => { var associations = x.Value.Where(assoc => { var handle = assoc.Association.AsInstanceOf <AkkaProtocolHandle>(); var drop = matchesQuarantine(handle); if (drop) { handle.Disassociate(); } return(!drop); }).ToList(); return(new KeyValuePair <IActorRef, List <InboundAssociation> >(x.Key, associations)); }).ToDictionary(k => k.Key, v => v.Value); }); Receive <Send>(send => { var recipientAddress = send.Recipient.Path.Address; Func <int?, IActorRef> createAndRegisterWritingEndpoint = refuseUid => _endpoints.RegisterWritableEndpoint(recipientAddress, CreateEndpoint(recipientAddress, send.Recipient.LocalAddressToUse, _transportMapping[send.Recipient.LocalAddressToUse], _settings, writing: true, handleOption: null, refuseUid: refuseUid), uid: null, refuseUid: refuseUid); // pattern match won't throw a NullReferenceException if one is returned by WritableEndpointWithPolicyFor _endpoints.WritableEndpointWithPolicyFor(recipientAddress).Match() .With <Pass>( pass => { pass.Endpoint.Tell(send); }) .With <Gated>(gated => { if (gated.TimeOfRelease.IsOverdue) { createAndRegisterWritingEndpoint(gated.RefuseUid).Tell(send); } else { Context.System.DeadLetters.Tell(send); } }) .With <WasGated>(wasGated => { createAndRegisterWritingEndpoint(wasGated.RefuseUid).Tell(send); }) .With <Quarantined>(quarantined => { // timeOfRelease is only used for garbage collection reasons, therefore it is ignored here. We still have // the Quarantined tombstone and we know what UID we don't want to accept, so use it. createAndRegisterWritingEndpoint(quarantined.Uid).Tell(send); }) .Default(msg => createAndRegisterWritingEndpoint(null).Tell(send)); }); Receive <InboundAssociation>(ia => HandleInboundAssociation(ia, false)); Receive <EndpointWriter.StoppedReading>(endpoint => AcceptPendingReader(endpoint.Writer)); Receive <Terminated>(terminated => { AcceptPendingReader(terminated.ActorRef); _endpoints.UnregisterEndpoint(terminated.ActorRef); HandleStashedInbound(terminated.ActorRef, writerIsIdle: false); }); Receive <EndpointWriter.TookOver>(tookover => RemovePendingReader(tookover.Writer, tookover.ProtocolHandle)); Receive <ReliableDeliverySupervisor.GotUid>(gotuid => { var policy = _endpoints.WritableEndpointWithPolicyFor(gotuid.RemoteAddress); var pass = policy as Pass; if (pass != null) { if (pass.RefuseUid == gotuid.Uid) { _endpoints.MarkAsQuarantined(gotuid.RemoteAddress, gotuid.Uid, Deadline.Now + _settings.QuarantineDuration); _eventPublisher.NotifyListeners(new QuarantinedEvent(gotuid.RemoteAddress, gotuid.Uid)); Context.Stop(pass.Endpoint); } else { _endpoints.RegisterWritableEndpointUid(gotuid.RemoteAddress, gotuid.Uid); } HandleStashedInbound(Sender, writerIsIdle: false); } else if (policy is WasGated) { var wg = (WasGated)policy; if (wg.RefuseUid == gotuid.Uid) { _endpoints.MarkAsQuarantined(gotuid.RemoteAddress, gotuid.Uid, Deadline.Now + _settings.QuarantineDuration); _eventPublisher.NotifyListeners(new QuarantinedEvent(gotuid.RemoteAddress, gotuid.Uid)); Context.Stop(pass.Endpoint); } else { _endpoints.RegisterWritableEndpointUid(gotuid.RemoteAddress, gotuid.Uid); } HandleStashedInbound(Sender, writerIsIdle: false); } else { // the GotUid might have lost the race with some failure } }); Receive <ReliableDeliverySupervisor.Idle>(idle => { HandleStashedInbound(Sender, writerIsIdle: true); }); Receive <Prune>(prune => _endpoints.Prune()); Receive <ShutdownAndFlush>(shutdown => { //Shutdown all endpoints and signal to Sender when ready (and whether all endpoints were shutdown gracefully) var sender = Sender; // The construction of the Task for shutdownStatus has to happen after the flushStatus future has been finished // so that endpoints are shut down before transports. var shutdownStatus = Task.WhenAll(_endpoints.AllEndpoints.Select( x => x.GracefulStop(_settings.FlushWait, EndpointWriter.FlushAndStop.Instance))).ContinueWith( result => { if (result.IsFaulted || result.IsCanceled) { if (result.Exception != null) { result.Exception.Handle(e => true); } return(false); } return(result.Result.All(x => x)); }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); shutdownStatus.ContinueWith(tr => Task.WhenAll(_transportMapping.Values.Select(x => x.Shutdown())).ContinueWith( result => { if (result.IsFaulted || result.IsCanceled) { if (result.Exception != null) { result.Exception.Handle(e => true); } return(false); } return(result.Result.All(x => x) && tr.Result); }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default)).Unwrap().PipeTo(sender); foreach (var handoff in _pendingReadHandoffs.Values) { handoff.Disassociate(DisassociateInfo.Shutdown); } //Ignore all other writes _normalShutdown = true; Become(Flushing); }); }