private void ValidateMessageHeader(IMessagingMessage message) { Logger.LogDebug("Validating message header"); var missingFields = new List <string>(); // the FromHerId is not checked by design. If this validation fails, we need to send a message back to the sender // but we have no idea who the sender is because the information is missing if (message.ToHerId == 0) { missingFields.Add(ServiceBusCore.ToHerIdHeaderKey); } if (string.IsNullOrEmpty(message.MessageFunction)) { missingFields.Add("Label"); } if (message.ApplicationTimestamp == DateTime.MinValue) { missingFields.Add(ServiceBusCore.ApplicationTimestampHeaderKey); } if (string.IsNullOrEmpty(message.ContentType)) { missingFields.Add("ContentType"); } if (missingFields.Count > 0) { throw new HeaderValidationException("One or more fields are missing") { Fields = missingFields } } ; }
/// <summary> /// Sends a prepared message /// </summary> /// <param name="logger"></param> /// <param name="message">The prepared message</param> /// <param name="queueType">The type of queue to use</param> /// <param name="userId"></param> /// <param name="xml">Optional xml content. This will be logged depending on the logging level.</param> /// <returns></returns> private async Task Send(ILogger logger, IMessagingMessage message, QueueType queueType, string userId = "99999999999", XDocument xml = null) { if (message == null) { throw new ArgumentNullException(nameof(message)); } IMessagingSender messageSender = null; try { messageSender = SenderPool.CreateCachedMessageSender(logger, message.To); await messageSender.SendAsync(message).ConfigureAwait(false); } catch (Exception ex) { logger.LogException("Cannot send message to service bus. Invalid endpoint.", ex); throw new MessagingException(ex.Message) { EventId = EventIds.Send }; } finally { if (messageSender != null) { SenderPool.ReleaseCachedMessageSender(logger, message.To); } } }
public Task SendAsync(IMessagingMessage message) { List <IMessagingMessage> queue; if (_factory.Qeueues.ContainsKey(_id) == false) { queue = new List <IMessagingMessage>(); _factory.Qeueues.Add(_id, queue); } else { queue = _factory.Qeueues[_id]; } var m = message as MockMessage; m.Queue = queue; //validate To queue so we can test errors connecting to queues. Different implementations throw different exceptions if (!string.IsNullOrEmpty(message.To) && message.To.StartsWith("Dialog_")) { throw new MessagingException(); } queue.Add(message); return(Task.CompletedTask); }
/// <summary> /// Sends a prepared message /// </summary> /// <param name="logger"></param> /// <param name="message">The prepared message</param> /// <param name="queueType">The type of queue to use</param> /// <param name="userId"></param> /// <param name="xml">Optional xml content. This will be logged depending on the logging level.</param> /// <returns></returns> private async Task Send(ILogger logger, IMessagingMessage message, QueueType queueType, string userId = "99999999999", XDocument xml = null) { if (message == null) { throw new ArgumentNullException(nameof(message)); } logger.LogStartSend(queueType, message.MessageFunction, message.FromHerId, message.ToHerId, message.MessageId, userId, xml); IMessagingSender messageSender = null; try { messageSender = SenderPool.CreateCachedMessageSender(logger, message.To); await messageSender.SendAsync(message).ConfigureAwait(false); } catch (Exception ex) { throw new MessagingException(ex.Message) { EventId = EventIds.Send }; } finally { if (messageSender != null) { SenderPool.ReleaseCachedMessageSender(logger, message.To); } } logger.LogEndSend(queueType, message.MessageFunction, message.FromHerId, message.ToHerId, message.MessageId, userId); }
private void ReportErrorOnRemoteCertificate(IMessagingMessage originalMessage, X509Certificate2 certificate, CertificateErrors error) { string errorCode; string description; EventId id; switch (error) { case CertificateErrors.None: // no error case CertificateErrors.Missing: // if the certificate is missing, it's because we don't know where it came from // and have no idea where to send an error message return; case CertificateErrors.StartDate: errorCode = "transport:expired-certificate"; description = "Invalid start date"; id = EventIds.RemoteCertificateStartDate; break; case CertificateErrors.EndDate: errorCode = "transport:expired-certificate"; description = "Invalid end date"; id = EventIds.RemoteCertificateEndDate; break; case CertificateErrors.Usage: errorCode = "transport:invalid-certificate"; description = "Invalid usage"; id = EventIds.RemoteCertificateUsage; break; case CertificateErrors.Revoked: errorCode = "transport:revoked-certificate"; description = "Certificate has been revoked"; id = EventIds.RemoteCertificateRevocation; break; case CertificateErrors.RevokedUnknown: errorCode = "transport:revoked-certificate"; description = "Unable to determine revocation status"; id = EventIds.RemoteCertificateRevocation; break; default: // since the value is bitcoded errorCode = "transport:invalid-certificate"; description = "More than one error with certificate"; id = EventIds.RemoteCertificate; break; } var additionalInformation = (error != CertificateErrors.Missing) || (error != CertificateErrors.None) ? new[] { certificate.Subject, certificate.Thumbprint } : new string[] { }; Core.ReportErrorToExternalSender(Logger, id, originalMessage, errorCode, description, additionalInformation); }
private XDocument HandlePayload(IMessagingMessage originalMessage, Stream bodyStream, string contentType, IncomingMessage incomingMessage, out bool contentWasSigned) { XDocument payload; if (contentType.Equals(ContentType.Text, StringComparison.OrdinalIgnoreCase) || contentType.Equals(ContentType.Soap, StringComparison.OrdinalIgnoreCase)) { contentWasSigned = false; // no certificates to validate payload = new NoMessageProtection().Unprotect(bodyStream, null)?.ToXDocument(); } else { contentWasSigned = true; // if we receive enrypted messages on the error queue, we have no idea what to do with them // Since this can be message we sent, it's encrypted with their certificate and we don't have that private key if (QueueType == QueueType.Error) { return(null); } // TODO: The whole part of validating the local certificates below should probably be move into // the IMessageProtection implementation, but since there are some constraints to properties on // IMessagingMessage we'll keep it here for now // in receive mode, we try to decrypt and validate content even if the certificates are invalid // invalid certificates are flagged to the application layer processing the decrypted message. // with the decrypted content, they may have a chance to figure out who sent it var validator = Core.CertificateValidator; // validate the local encryption certificate and, if present, the local legacy encryption certificate incomingMessage.DecryptionError = validator == null ? CertificateErrors.None : validator.Validate(Core.MessageProtection.EncryptionCertificate, X509KeyUsageFlags.DataEncipherment); // in earlier versions of Helsenorge.Messaging we removed the message, but we should rather // want it to be dead lettered since this is a temp issue that should be fixed locally. ReportErrorOnLocalCertificate(originalMessage, Core.MessageProtection.EncryptionCertificate, incomingMessage.DecryptionError, false); if (Core.MessageProtection.LegacyEncryptionCertificate != null) { // this is optional information that should only be in effect durin a short transition period incomingMessage.LegacyDecryptionError = validator == null ? CertificateErrors.None : validator.Validate(Core.MessageProtection.LegacyEncryptionCertificate, X509KeyUsageFlags.DataEncipherment); // if someone forgets to remove the legacy configuration, we log an error message but don't remove it ReportErrorOnLocalCertificate(originalMessage, Core.MessageProtection.LegacyEncryptionCertificate, incomingMessage.LegacyDecryptionError, false); } // validate remote signature certificate var signature = incomingMessage.CollaborationAgreement?.SignatureCertificate; incomingMessage.SignatureError = validator == null ? CertificateErrors.None : validator.Validate(signature, X509KeyUsageFlags.NonRepudiation); ReportErrorOnRemoteCertificate(originalMessage, signature, incomingMessage.SignatureError); // decrypt the message and validate the signatureS payload = Core.MessageProtection.Unprotect(bodyStream, signature)?.ToXDocument(); } return(payload); }
/// <summary> /// Removes the message from the queue as part of normal operation /// </summary> /// <param name="message"></param> internal static void RemoveProcessedMessageFromQueue(IMessagingMessage message) { if (message == null) { throw new ArgumentNullException(nameof(message)); } message.Complete(); }
/// <summary> /// Removes the message from the queue as part of normal operation /// </summary> /// <param name="logger"></param> /// <param name="message"></param> internal static void RemoveProcessedMessageFromQueue(ILogger logger, IMessagingMessage message) { if (message == null) { throw new ArgumentNullException(nameof(message)); } logger.LogRemoveMessageFromQueueNormal(message.MessageId); message.Complete(); }
private void ReportErrorOnLocalCertificate(IMessagingMessage originalMessage, X509Certificate2 certificate, CertificateErrors error, bool removeMessage) { string description; EventId id; switch (error) { case CertificateErrors.None: return; // no error case CertificateErrors.StartDate: description = "Invalid start date"; id = EventIds.LocalCertificateStartDate; break; case CertificateErrors.EndDate: description = "Invalid end date"; id = EventIds.LocalCertificateEndDate; break; case CertificateErrors.Usage: description = "Invalid usage"; id = EventIds.LocalCertificateUsage; break; case CertificateErrors.Revoked: description = "Certificate has been revoked"; id = EventIds.LocalCertificateRevocation; break; case CertificateErrors.RevokedUnknown: description = "Unable to determine revocation status"; id = EventIds.LocalCertificateRevocation; break; case CertificateErrors.Missing: description = "Certificate is missing"; id = EventIds.LocalCertificate; break; default: // since the value is bitcoded description = "More than one error with certificate"; id = EventIds.LocalCertificate; break; } Logger.LogError(id, null, "Description: {Description} Subject: {Subject} Thumbprint: {Thumbprint}", description, certificate?.Subject, certificate?.Thumbprint); if (removeMessage) { ServiceBusCore.RemoveMessageFromQueueAfterError(Logger, originalMessage); } }
/// <summary> /// Sends a message to the remote sender with information about what is wrong. /// Loggs information to our logs. /// Removes message from processing queue since there is no point in processing it again. /// </summary> /// <param name="logger"></param> /// <param name="id">The event id that error should be logged with</param> /// <param name="originalMessage"></param> /// <param name="errorCode"></param> /// <param name="description"></param> /// <param name="additionalData"></param> /// <param name="ex"></param> internal void ReportErrorToExternalSender( ILogger logger, EventId id, IMessagingMessage originalMessage, string errorCode, string description, IEnumerable <string> additionalData, Exception ex = null) { logger.LogError(id, ex, description); Task.WaitAll(SendError(logger, originalMessage, errorCode, description, additionalData)); RemoveMessageFromQueueAfterError(logger, originalMessage); }
private async Task <CollaborationProtocolProfile> ResolveProfile(IMessagingMessage message) { Guid id; if (Guid.TryParse(message.CpaId, out id) && (id != Guid.Empty)) { return(await Core.CollaborationProtocolRegistry.FindAgreementByIdAsync(Logger, id).ConfigureAwait(false)); } return // try first to find an agreement (await Core.CollaborationProtocolRegistry.FindAgreementForCounterpartyAsync(Logger, message.FromHerId).ConfigureAwait(false) ?? // if we cannot find that, we fallback to protocol (which may return a dummy protocol if things are really missing in AR) await Core.CollaborationProtocolRegistry.FindProtocolForCounterpartyAsync(Logger, message.FromHerId).ConfigureAwait(false)); }
public async Task SendAsync(IMessagingMessage message) { if (message == null) { throw new ArgumentNullException(nameof(message)); } var brokeredMessage = message.OriginalObject as BrokeredMessage; if (brokeredMessage == null) { throw new InvalidOperationException("OriginalObject is not a Brokered message"); } await _implementation.SendAsync(brokeredMessage).ConfigureAwait(false); }
private async Task <CollaborationProtocolProfile> ResolveProfile(IMessagingMessage message) { // if we receive an error message then CPA isn't needed because we're not decrypting the message and then the CPA info isn't needed if (QueueType == QueueType.Error) { return(null); } if (Guid.TryParse(message.CpaId, out Guid id) && (id != Guid.Empty)) { return(await Core.CollaborationProtocolRegistry.FindAgreementByIdAsync(Logger, id).ConfigureAwait(false)); } return // try first to find an agreement (await Core.CollaborationProtocolRegistry.FindAgreementForCounterpartyAsync(Logger, message.FromHerId).ConfigureAwait(false) ?? // if we cannot find that, we fallback to protocol (which may return a dummy protocol if things are really missing in AR) await Core.CollaborationProtocolRegistry.FindProtocolForCounterpartyAsync(Logger, message.FromHerId).ConfigureAwait(false)); }
private XDocument HandlePayload(IMessagingMessage originalMessage, Stream bodyStream, string contentType, IncomingMessage incomingMessage, out bool contentWasSigned) { XDocument payload; if (contentType.Equals(ContentType.Text, StringComparison.OrdinalIgnoreCase) || contentType.Equals(ContentType.Soap, StringComparison.OrdinalIgnoreCase)) { contentWasSigned = false; // no certificates to validate payload = new NoMessageProtection().Unprotect(bodyStream, null, null, null); } else { contentWasSigned = true; // if we receive enrypted messages on the error queue, we have no idea what to do with them // Since this can be message we sent, it's encrypted with their certificate and we don't have that private key if (QueueType == QueueType.Error) { return(null); } // in receive mode, we try to decrypt and validate content even if the certificates are invalid // invalid certificates are flagged to the application layer processing the decrypted message. // with the decrypted content, they may have a chance to figure out who sent it var decryption = Core.Settings.DecryptionCertificate.Certificate; var signature = incomingMessage.CollaborationAgreement?.SignatureCertificate; var legacyDecryption = Core.Settings.LegacyDecryptionCertificate?.Certificate; incomingMessage.DecryptionError = Core.DefaultCertificateValidator.Validate(decryption, X509KeyUsageFlags.DataEncipherment); ReportErrorOnLocalCertificate(originalMessage, decryption, incomingMessage.DecryptionError, true); incomingMessage.SignatureError = Core.DefaultCertificateValidator.Validate(signature, X509KeyUsageFlags.NonRepudiation); ReportErrorOnRemoteCertificate(originalMessage, signature, incomingMessage.SignatureError); if (legacyDecryption != null) { // this is optional information that should only be in effect durin a short transition period incomingMessage.LegacyDecryptionError = Core.DefaultCertificateValidator.Validate(legacyDecryption, X509KeyUsageFlags.DataEncipherment); // if someone forgets to remove the legacy configuration, we log an error message but don't remove it ReportErrorOnLocalCertificate(originalMessage, legacyDecryption, incomingMessage.LegacyDecryptionError, false); } payload = Core.DefaultMessageProtection.Unprotect(bodyStream, decryption, signature, legacyDecryption); } return(payload); }
public async Task SendAsync(IMessagingMessage message) { Debug.Assert(message is OutgoingHttpMessage); var httpClient = new HttpClient(); var response = await httpClient.PostAsync( new Uri(new Uri(_url), _id), new StringContent( (message as OutgoingHttpMessage).CreateHttpBody().ToString() ) ).ConfigureAwait(false); // TODO: MUST FIX caller code! if (response.StatusCode != System.Net.HttpStatusCode.OK) { var responseContent = await response.Content.ReadAsStringAsync(); throw new ArgumentException($"Error from AMQP/HTTP server: {responseContent}"); } }
/// <summary> /// Called to process message /// </summary> /// <param name="rawMessage">The message from the queue</param> /// <param name="message">The refined message data. All information should now be present</param> protected override void NotifyMessageProcessingReady(IMessagingMessage rawMessage, IncomingMessage message) { var reply = MessagingNotification.NotifySynchronousMessageReceived(message); if (reply == null) { throw new InvalidOperationException($"Message handler for function {message.MessageFunction} returned null"); } var outgoingMessage = new OutgoingMessage() { ToHerId = message.FromHerId, Payload = reply, MessageFunction = message.MessageFunction, MessageId = Guid.NewGuid().ToString() }; Task.WaitAll(Core.Send(Logger, outgoingMessage, QueueType.SynchronousReply, rawMessage.ReplyTo, rawMessage.CorrelationId)); }
public async Task SendAsync(IMessagingMessage message) { Debug.Assert(message is OutgoingHttpMessage); var httpClient = new HttpClient(); var request = new HttpRequestMessage(HttpMethod.Post, new Uri(new Uri(_url), _id)); request.Headers.Add(HttpServiceBusReceiver.CLIENT_HEADER_NAME, HttpServiceBusReceiver.GetClientHeaderValue()); request.Content = new StringContent( (message as OutgoingHttpMessage).CreateHttpBody().ToString() ); var response = await httpClient.SendAsync(request).ConfigureAwait(false); if (response.StatusCode != System.Net.HttpStatusCode.OK) { var responseContent = await response.Content.ReadAsStringAsync(); throw new ArgumentException($"Error from AMQP/HTTP server: {responseContent}"); } }
public Task SendAsync(IMessagingMessage message) { List <IMessagingMessage> queue; if (_factory.Qeueues.ContainsKey(_id) == false) { queue = new List <IMessagingMessage>(); _factory.Qeueues.Add(_id, queue); } else { queue = _factory.Qeueues[_id]; } var m = message as MockMessage; m.Queue = queue; queue.Add(message); return(Task.CompletedTask); }
public void NotifyHandledException(IMessagingMessage message, Exception ex) { }
public void NotifyErrorMessageReceived(IMessagingMessage message) { }
/// <summary> /// Called to process message /// </summary> /// <param name="rawMessage">The message from the queue</param> /// <param name="message">The refined message data. All information should now be present</param> protected override void NotifyMessageProcessingReady(IMessagingMessage rawMessage, IncomingMessage message) { Logger.LogBeforeNotificationHandler(nameof(MessagingNotification.NotifySynchronousMessageReceived), message.MessageFunction, message.FromHerId, message.ToHerId, message.MessageId); MessagingNotification.NotifySynchronousMessageReceived(message); Logger.LogAfterNotificationHandler(nameof(MessagingNotification.NotifySynchronousMessageReceived), message.MessageFunction, message.FromHerId, message.ToHerId, message.MessageId); }
/// <summary> /// Called to process message /// </summary> /// <param name="rawMessage">The message from the queue</param> /// <param name="message">The refined message data. All information should now be present</param> protected abstract void NotifyMessageProcessingReady(IMessagingMessage rawMessage, IncomingMessage message);
public static void LogRemoveMessageFromQueueNormal(this ILogger logger, IMessagingMessage message, string queueName) { RemoveMessageFromQueueNormal(logger, message.MessageId, message.FromHerId, queueName, message.CorrelationId, null); }
/// <summary> /// Called to process message /// </summary> /// <param name="rawMessage">The message from the queue</param> /// <param name="message">The refined message data. All information should now be present</param> protected override void NotifyMessageProcessingReady(IMessagingMessage rawMessage, IncomingMessage message) { MessagingNotification.NotifyAsynchronousMessageReceived(message); }
private async Task <IncomingMessage> HandleRawMessage(IMessagingMessage message, bool alwaysRemoveMessage) { if (message == null) { return(null); } Stream bodyStream = null; try { var incomingMessage = new IncomingMessage() { MessageFunction = message.MessageFunction, FromHerId = message.FromHerId, ToHerId = message.ToHerId, MessageId = message.MessageId, CorrelationId = message.CorrelationId, EnqueuedTimeUtc = message.EnqueuedTimeUtc, RenewLock = message.RenewLock, DeliveryCount = message.DeliveryCount }; NotifyMessageProcessingStarted(incomingMessage); Logger.LogStartReceive(QueueType, incomingMessage); // we cannot dispose of the stream before we have potentially cloned the message for error use bodyStream = message.GetBody(); ValidateMessageHeader(message); // we need the certificates for decryption and certificate use incomingMessage.CollaborationAgreement = await ResolveProfile(message).ConfigureAwait(false); var payload = HandlePayload(message, bodyStream, message.ContentType, incomingMessage, out bool contentWasSigned); incomingMessage.ContentWasSigned = contentWasSigned; if (payload != null) { if (Core.LogPayload) { Logger.LogDebug(payload.ToString()); } incomingMessage.Payload = payload; } NotifyMessageProcessingReady(message, incomingMessage); ServiceBusCore.RemoveProcessedMessageFromQueue(message); Logger.LogRemoveMessageFromQueueNormal(message, QueueName); NotifyMessageProcessingCompleted(incomingMessage); Logger.LogEndReceive(QueueType, incomingMessage); return(incomingMessage); } catch (SecurityException ex) { Core.ReportErrorToExternalSender(Logger, EventIds.RemoteCertificate, message, "transport:invalid-certificate", ex.Message, null, ex); MessagingNotification.NotifyHandledException(message, ex); } catch (HeaderValidationException ex) { Core.ReportErrorToExternalSender(Logger, EventIds.MissingField, message, "transport:invalid-field-value", ex.Message, ex.Fields); MessagingNotification.NotifyHandledException(message, ex); } catch (XmlSchemaValidationException ex) // reportable error from message handler (application) { Core.ReportErrorToExternalSender(Logger, EventIds.NotXml, message, "transport:not-well-formed-xml", ex.Message, null, ex); MessagingNotification.NotifyHandledException(message, ex); } catch (ReceivedDataMismatchException ex) // reportable error from message handler (application) { Core.ReportErrorToExternalSender(Logger, EventIds.DataMismatch, message, "transport:invalid-field-value", ex.Message, new[] { ex.ExpectedValue, ex.ReceivedValue }, ex); MessagingNotification.NotifyHandledException(message, ex); } catch (NotifySenderException ex) // reportable error from message handler (application) { Core.ReportErrorToExternalSender(Logger, EventIds.ApplicationReported, message, "transport:internal-error", ex.Message, null, ex); MessagingNotification.NotifyHandledException(message, ex); } catch (SenderHerIdMismatchException ex) // reportable error from message handler (application) { Core.ReportErrorToExternalSender(Logger, EventIds.DataMismatch, message, "abuse:spoofing-attack", ex.Message, null, ex); MessagingNotification.NotifyHandledException(message, ex); } catch (PayloadDeserializationException ex) // from parsing to XML, reportable exception { Core.ReportErrorToExternalSender(Logger, EventIds.ApplicationReported, message, "transport:not-well-formed-xml", ex.Message, null, ex); MessagingNotification.NotifyHandledException(message, ex); } catch (AggregateException ex) when(ex.InnerException is MessagingException && ((MessagingException)ex.InnerException).EventId.Id == EventIds.Send.Id) { Core.ReportErrorToExternalSender(Logger, EventIds.ApplicationReported, message, "transport:invalid-field-value", "Invalid value in field: 'ReplyTo'", null, ex); MessagingNotification.NotifyHandledException(message, ex); } catch (UnsupportedMessageException ex) // reportable error from message handler (application) { Core.ReportErrorToExternalSender(Logger, EventIds.ApplicationReported, message, "transport:unsupported-message", ex.Message, null, ex); MessagingNotification.NotifyHandledException(message, ex); } catch (Exception ex) // unknown error { message.AddDetailsToException(ex); Logger.LogError(EventIds.UnknownError, null, $"Message processing failed. Keeping lock until it times out and we can try again. Message expires at UTC {message.ExpiresAtUtc}"); Logger.LogException("Unknown error", ex); // if something unknown goes wrong, we want to retry the message after a delay // we don't call Complete() or Abandon() since that will cause the message to be availble again // chances are that the failure may still be around // the Defer() method requires us to store the sequence id, but we don't have a place to store it // the option then is to let the lock time-out. This will happen after a couple of minutes and the // message becomes available again. 10 retries = 10 timeouts before it gets added to DLQ if (alwaysRemoveMessage) { ServiceBusCore.RemoveMessageFromQueueAfterError(Logger, message); } MessagingNotification.NotifyUnhandledException(message, ex); } finally { bodyStream?.Dispose(); message.Dispose(); } return(null); }
void IMessagingNotification.NotifyErrorMessageReceived(IMessagingMessage message) { _logger.LogDebug("NotifyErrorMessageReceived"); _onErrorMessageReceived?.Invoke(message); }
void IMessagingNotification.NotifyUnhandledException(IMessagingMessage message, Exception ex) { _logger.LogDebug("NotifyUnhandledException"); _onUnhandledException?.Invoke(message, ex); }
/// <summary> /// Called to process message /// </summary> /// <param name="rawMessage">The message from the queue</param> /// <param name="message">The refined message data. All information should now be present</param> protected override void NotifyMessageProcessingReady(IMessagingMessage rawMessage, IncomingMessage message) { Logger.LogDebug("NotifyMessageProcessingReady"); MessagingNotification.NotifySynchronousMessageReceived(message); }
/// <summary> /// Called to process message /// </summary> /// <param name="rawMessage">The message from the queue</param> /// <param name="message">The refined message data. All information should now be present</param> protected override void NotifyMessageProcessingReady(IMessagingMessage rawMessage, IncomingMessage message) { if (rawMessage == null) { throw new ArgumentNullException(nameof(rawMessage)); } if (message == null) { throw new ArgumentNullException(nameof(message)); } var stringBuilder = new StringBuilder(); stringBuilder.Append($"Label: {message.MessageFunction} "); // we have received a soap fault if (message.MessageFunction.Equals(ServiceBusCore.SoapFaultLabel, StringComparison.OrdinalIgnoreCase) && (message.Payload != null)) { XNamespace soapNs = "http://www.w3.org/2003/05/soap-envelope"; XNamespace dialogNs = "http://www.kith.no/xmlstds/digitaldialog/2013-10-08"; if (message.Payload.Root != null) { var bodyNode = message.Payload.Root.Element(soapNs + "Body"); var faultNode = bodyNode?.Element(soapNs + "Fault"); if (faultNode != null) { var valueNode = faultNode.Descendants(soapNs + "Value").FirstOrDefault(); if (valueNode != null) { stringBuilder.Append($"FaultCode: {valueNode.Value} "); } var reasonNode = faultNode.Descendants(soapNs + "Text").FirstOrDefault(); if (reasonNode != null) { stringBuilder.Append($"FaultReason: \"{reasonNode.Value}\" "); } var messageIdNode = faultNode.Descendants(dialogNs + "messageId").FirstOrDefault(); if (messageIdNode != null) { stringBuilder.Append($"MessageId: {messageIdNode.Value} "); } var timestampNode = faultNode.Descendants(dialogNs + "applicationTimeStamp").FirstOrDefault(); if (timestampNode != null) { stringBuilder.Append($"ApplicationTimeStamp: {timestampNode.Value} "); } } } } else // we received a message where error codes are stored in properties { foreach (var property in rawMessage.Properties) { stringBuilder.Append($"{property.Key}: {property.Value} "); } } Logger.LogExternalReportedError(stringBuilder.ToString()); Logger.LogBeforeNotificationHandler(nameof(MessagingNotification.NotifyErrorMessageReceived), message.MessageFunction, message.FromHerId, message.ToHerId, message.MessageId); MessagingNotification.NotifyErrorMessageReceived(rawMessage); Logger.LogAfterNotificationHandler(nameof(MessagingNotification.NotifyErrorMessageReceived), message.MessageFunction, message.FromHerId, message.ToHerId, message.MessageId); }
/// <summary> /// Sends an error message /// </summary> /// <param name="logger"></param> /// <param name="originalMessage">The original message the error is in response to</param> /// <param name="errorCode">The error code to report</param> /// <param name="errorDescription">The error description to report</param> /// <param name="additionalData">Additional information to include</param> /// <returns></returns> private async Task SendError(ILogger logger, IMessagingMessage originalMessage, string errorCode, string errorDescription, IEnumerable <string> additionalData) //TODO: Sjekk at SendError fungerer med Http-meldinger { if (originalMessage == null) { throw new ArgumentNullException(nameof(originalMessage)); } if (string.IsNullOrEmpty(errorCode)) { throw new ArgumentNullException(nameof(errorCode)); } if (string.IsNullOrEmpty(errorDescription)) { throw new ArgumentNullException(nameof(errorDescription)); } if (originalMessage.FromHerId <= 0) { logger.LogError(EventIds.MissingField, "FromHerId is missing. No idea where to send the error"); return; } var clonedMessage = originalMessage.Clone(); // update some properties on the cloned message clonedMessage.To = await ConstructQueueName(logger, originalMessage.FromHerId, QueueType.Error); // change target clonedMessage.TimeToLive = Settings.Error.TimeToLive; clonedMessage.FromHerId = originalMessage.ToHerId; clonedMessage.ToHerId = originalMessage.FromHerId; if (clonedMessage.Properties.ContainsKey(OriginalMessageIdHeaderKey) == false) { clonedMessage.Properties.Add(OriginalMessageIdHeaderKey, originalMessage.MessageId); } if (clonedMessage.Properties.ContainsKey(ReceiverTimestampHeaderKey) == false) { clonedMessage.Properties.Add(ReceiverTimestampHeaderKey, DateTime.Now.ToString(DateTimeFormatInfo.InvariantInfo)); } if (clonedMessage.Properties.ContainsKey(ErrorConditionHeaderKey) == false) { clonedMessage.Properties.Add(ErrorConditionHeaderKey, errorCode); } if (clonedMessage.Properties.ContainsKey(ErrorDescriptionHeaderKey) == false) { clonedMessage.Properties.Add(ErrorDescriptionHeaderKey, errorDescription); } var additionDataValue = "None"; if (additionalData != null) { var sb = new StringBuilder(); foreach (var item in additionalData) { if (string.IsNullOrEmpty(item) == false) { sb.Append($"{item};"); } } additionDataValue = sb.ToString(); if (clonedMessage.Properties.ContainsKey(ErrorConditionDataHeaderKey) == false) { clonedMessage.Properties.Add(ErrorConditionDataHeaderKey, additionDataValue); } } logger.LogError("Reporting error to sender. ErrorCode: {0} ErrorDescription: {1} AdditionalData: {2}", errorCode, errorDescription, additionDataValue); await Send(logger, clonedMessage, QueueType.Error).ConfigureAwait(false); }