void ClientOperationComplete(IAsyncResult result) { MessageRpc messageRpc = this.service.SessionMessages[this.sessionMessageIndex]; SendOperation currentDest = messageRpc.Operations[this.destinationIndex]; this.client.EndOperation(result); currentDest.TransmitSucceeded(this.service.GetTransactionForSending(messageRpc)); if (TD.RoutingServiceTransmitSucceededIsEnabled()) { TD.RoutingServiceTransmitSucceeded(messageRpc.EventTraceActivity, messageRpc.UniqueID, this.destinationIndex.ToString(TD.Culture), currentDest.CurrentEndpoint.ToString()); } MoveToNextClientOperation(messageRpc.Operations.Count); }
// A Sessionful channel failed when closing, find all messages that went on that // session/channel and move them to their backup endpoints bool HandleCloseFailure(Exception e) { if (!(e is CommunicationException || e is TimeoutException)) { return(false); } if (TD.RoutingServiceCloseFailedIsEnabled()) { TD.RoutingServiceCloseFailed(this.client.Key.ToString(), e); } this.channelExtension.SessionChannels.AbortChannel(this.client.Key); if (this.service.SessionMessages.Count == 0) { //All messages have been sent and we're non-transactional Fx.Assert(!this.service.ChannelExtension.TransactedReceiveEnabled, "Should only happen for non-transactional cases"); return(true); } foreach (MessageRpc messageRpc in this.service.SessionMessages) { for (this.destinationIndex = 0; this.destinationIndex < messageRpc.Operations.Count; this.destinationIndex++) { SendOperation sendOperation = messageRpc.Operations[this.destinationIndex]; if (client.Key.Equals(sendOperation.CurrentEndpoint)) { if (!sendOperation.TryMoveToAlternate(e)) { return(false); } if (TD.RoutingServiceMovedToBackupIsEnabled()) { TD.RoutingServiceMovedToBackup(messageRpc.EventTraceActivity, messageRpc.UniqueID, this.destinationIndex.ToString(TD.Culture), sendOperation.CurrentEndpoint.ToString()); } } } } this.ResetState(); return(true); }
bool StartProcessing() { bool callAgain = false; SendOperation sendOperation = this.messageRpc.Operations[0]; this.currentClient = this.service.GetOrCreateClient <TContract>(sendOperation.CurrentEndpoint, this.messageRpc.Impersonating); if (TD.RoutingServiceTransmittingMessageIsEnabled()) { TD.RoutingServiceTransmittingMessage(this.messageRpc.EventTraceActivity, this.messageRpc.UniqueID, "0", this.currentClient.Key.ToString()); } try { if (messageRpc.Transaction != null && sendOperation.HasAlternate) { throw FxTrace.Exception.AsError(new ConfigurationErrorsException(SR.ErrorHandlingNotSupportedReqReplyTxn(this.messageRpc.OperationContext.Channel.LocalAddress))); } // We always work on cloned message when there are backup endpoints to handle exception cases Message message; if (sendOperation.AlternateEndpointCount > 0) { message = messageRpc.CreateBuffer().CreateMessage(); } else { message = messageRpc.Message; } sendOperation.PrepareMessage(message); IAsyncResult result = null; using (this.PrepareTransactionalCall(messageRpc.Transaction)) { IDisposable impersonationContext = null; try { //Perform the assignment in a finally block so it won't be interrupted asynchronously try { } finally { impersonationContext = messageRpc.PrepareCall(); } result = this.currentClient.BeginOperation(message, messageRpc.Transaction, this.PrepareAsyncCompletion(operationCallback), this); } finally { if (impersonationContext != null) { impersonationContext.Dispose(); } } } if (this.CheckSyncContinue(result)) { if (this.OperationComplete(result)) { this.Complete(this.allCompletedSync); } else { callAgain = true; } } } catch (Exception exception) { if (Fx.IsFatal(exception)) { throw; } if (!this.HandleClientOperationFailure(exception)) { throw; } callAgain = true; } return(callAgain); }
bool HandleClientOperationFailure(Exception exception) { SendOperation sendOperation = this.messageRpc.Operations[0]; if (TD.RoutingServiceTransmitFailedIsEnabled()) { TD.RoutingServiceTransmitFailed(this.messageRpc.EventTraceActivity, sendOperation.CurrentEndpoint.ToString(), exception); } if (!(exception is CommunicationException || exception is TimeoutException)) { //We only move to backup for CommunicationExceptions and TimeoutExceptions return(false); } if ((exception is CommunicationObjectAbortedException || exception is CommunicationObjectFaultedException) && !this.service.ChannelExtension.HasSession) { // Messages on a non sessionful channel share outbound connections and can // fail due to other messages failing on the same channel if (messageRpc.Transaction == null && !this.abortedRetry) { //No session and non transactional, retry the message 1 time (before moving to backup) this.abortedRetry = true; return(true); } } else if (exception is EndpointNotFoundException) { // The channel may not fault for this exception for bindings other than netTcpBinding // We abort the channel in that case. We proactively clean up so that we don't have to cleanup later SessionChannels sessionChannels = this.service.GetSessionChannels(this.messageRpc.Impersonating); if (sessionChannels != null) { sessionChannels.AbortChannel(sendOperation.CurrentEndpoint); } } else if (exception is MessageSecurityException) { // The service may have been stopped and restarted without the routing service knowledge. // When we try to use a cached channel to the service, the channel can fault due to this exception // The faulted channel gets cleaned up and we retry one more time only when service has backup // If there is no backup, we do not retry since we do not create a buffered message to prevent performance degradation if (!this.abortedRetry && (sendOperation.AlternateEndpointCount > 0)) { this.abortedRetry = true; return(true); } } else if (exception is ProtocolException) { // This exception may happen when the current cached channel was closed due to end service recycles. // We abort the channel in this case and clean it up from the session. // We will then retry the request one more time only. In retried request, it will create a new channel because the cached channel has been cleaned up. if (!this.abortedRetry) { SessionChannels sessionChannels = this.service.GetSessionChannels(this.messageRpc.Impersonating); if (sessionChannels != null) { this.abortedRetry = true; sessionChannels.AbortChannel(sendOperation.CurrentEndpoint); return(true); } } } if (sendOperation.TryMoveToAlternate(exception)) { if (TD.RoutingServiceMovedToBackupIsEnabled()) { TD.RoutingServiceMovedToBackup(this.messageRpc.EventTraceActivity, messageRpc.UniqueID, "0", sendOperation.CurrentEndpoint.ToString()); } return(true); } return(false); }
bool HandleClientOperationFailure(Exception e) { if (TD.RoutingServiceTransmitFailedIsEnabled()) { TD.RoutingServiceTransmitFailed(null, this.client.Key.ToString(), e); } if (!(e is CommunicationException || e is TimeoutException)) { //We only move to backup for CommunicationExceptions and TimeoutExceptions return(false); } bool canHandle; MessageRpc messageRpc = this.service.SessionMessages[this.sessionMessageIndex]; SendOperation sendOperation = messageRpc.Operations[this.destinationIndex]; if ((e is CommunicationObjectAbortedException || e is CommunicationObjectFaultedException) && !this.channelExtension.HasSession) { // Messages on a non sessionful channel share outbound connections and can // fail due to other messages failing on the same channel bool canRetry = (this.channelExtension.ReceiveContextEnabled || !this.channelExtension.TransactedReceiveEnabled); if (canRetry && !this.abortedRetry) { //No session and ReceiveContext or non transactional, retry the message 1 time (before moving to backup) this.abortedRetry = true; this.ResetState(); return(true); } } else if (e is EndpointNotFoundException) { // The channel may not fault for this exception for bindings other than netTcpBinding // We abort the channel in that case. We proactively clean up so that we don't have to cleanup later SessionChannels sessionChannels = this.service.GetSessionChannels(messageRpc.Impersonating); if (sessionChannels != null) { sessionChannels.AbortChannel(sendOperation.CurrentEndpoint); } } else if (e is MessageSecurityException) { // The service may have been stopped and restarted without the routing service knowledge. // When we try to use a cached channel to the service, the channel can fault due to this exception // The faulted channel gets cleaned up and we retry one more time only when service has backup // If there is no backup, we do not retry since we do not create a buffered message to prevent performance degradation if (!this.abortedRetry && (sendOperation.AlternateEndpointCount > 0)) { this.abortedRetry = true; this.ResetState(); return(true); } } if (sendOperation.TryMoveToAlternate(e)) { if (TD.RoutingServiceMovedToBackupIsEnabled()) { TD.RoutingServiceMovedToBackup(messageRpc.EventTraceActivity, messageRpc.UniqueID, this.destinationIndex.ToString(TD.Culture), sendOperation.CurrentEndpoint.ToString()); } this.ResetState(); canHandle = true; } else if (this.service.GetTransactionForSending(messageRpc) == null) { // This is OneWay with no Transaction... // store this exception for when we complete, but continue any multicasting this.service.SessionException = e; // Mark the SendOperation as 'Sent' because there's no more work we can do (non-tx and no more backups) sendOperation.TransmitSucceeded(null); if (this.channelExtension.HasSession) { this.channelExtension.SessionChannels.AbortChannel(this.client.Key); } this.MoveToNextClientOperation(messageRpc.Operations.Count); canHandle = true; } else { canHandle = false; } return(canHandle); }
bool SendToCurrentClient() { MessageRpc messageRpc = this.service.SessionMessages[this.sessionMessageIndex]; SendOperation sendOperation = messageRpc.Operations[this.destinationIndex]; if (sendOperation.Sent) { this.MoveToNextClientOperation(messageRpc.Operations.Count); return(true); } else if (!this.channelExtension.ReceiveContextEnabled && this.channelExtension.TransactedReceiveEnabled && sendOperation.HasAlternate) { // We can't do error handling for oneway Transactional unless there's RC. throw FxTrace.Exception.AsError(new ConfigurationErrorsException(SR.ErrorHandlingNotSupportedTxNoRC(messageRpc.OperationContext.Channel.LocalAddress))); } RoutingEndpointTrait endpointTrait = sendOperation.CurrentEndpoint; this.client = this.service.GetOrCreateClient <TContract>(endpointTrait, messageRpc.Impersonating); try { // We always work on cloned message when there are backup endpoints to handle exception cases Message message; if (messageRpc.Operations.Count == 1 && sendOperation.AlternateEndpointCount == 0) { message = messageRpc.Message; } else { message = messageRpc.CreateBuffer().CreateMessage(); } sendOperation.PrepareMessage(message); IAsyncResult result; if (TD.RoutingServiceTransmittingMessageIsEnabled()) { TD.RoutingServiceTransmittingMessage(messageRpc.EventTraceActivity, messageRpc.UniqueID, this.destinationIndex.ToString(TD.Culture), this.client.Key.ToString()); } Transaction transaction = this.service.GetTransactionForSending(messageRpc); using (this.PrepareTransactionalCall(transaction)) { IDisposable impersonationContext = null; try { //Perform the assignment in a finally block so it won't be interrupted asynchronously try { } finally { impersonationContext = messageRpc.PrepareCall(); } result = this.client.BeginOperation(message, transaction, this.PrepareAsyncCompletion(clientOperationCallback), this); } finally { if (impersonationContext != null) { impersonationContext.Dispose(); } } } if (this.CheckSyncContinue(result)) { this.ClientOperationComplete(result); return(true); } else { return(false); } } catch (Exception exception) { if (Fx.IsFatal(exception)) { throw; } //See if we can handle this Exception... if (this.HandleClientOperationFailure(exception)) { return(true); } throw; } }