Beispiel #1
0
        /// <summary>
        /// Provide the fault
        /// </summary>
        public bool ProvideFault(Exception error, RestResponseMessage response)
        {
            var errCode = WebErrorUtility.ClassifyException(error, true);
            var hdlr    = ApplicationContext.Current.GetService <IAppletManagerService>().Applets.SelectMany(o => o.ErrorAssets).FirstOrDefault(o => o.ErrorCode == errCode);

#if DEBUG
            var ie = error;
            while (ie != null)
            {
                this.m_tracer.TraceError("{0} - ({1}){2} - {3}", error == ie ? "" : "Caused By",
                                         RestOperationContext.Current.EndpointOperation?.Description.InvokeMethod.Name,
                                         ie.GetType().FullName, ie.Message);
                ie = ie.InnerException;
            }
#else
            if (error is TargetInvocationException)
            {
                this.m_tracer.TraceError("{0} - {1} / {2}", RestOperationContext.Current.EndpointOperation.Description.InvokeMethod.Name, error.Message, error.InnerException?.Message);
            }
            else
            {
                this.m_tracer.TraceError("{0} - {1}", RestOperationContext.Current.EndpointOperation.Description.InvokeMethod.Name, error.Message);
            }
#endif

            // Grab the asset handler
            try
            {
                if (hdlr != null)
                {
                    response.Body = new MemoryStream(new byte[0]);
                    RestOperationContext.Current.OutgoingResponse.Redirect(hdlr.Asset);
                }
                else
                {
                    RestOperationContext.Current.OutgoingResponse.StatusCode = errCode;
                    using (var sr = new StreamReader(typeof(AgsWebErrorHandlerServiceBehavior).Assembly.GetManifestResourceStream("SanteDB.DisconnectedClient.Ags.Resources.GenericError.html")))
                    {
                        string errRsp = sr.ReadToEnd().Replace("{status}", response.StatusCode.ToString())
                                        .Replace("{description}", response.StatusDescription)
                                        .Replace("{type}", error.GetType().Name)
                                        .Replace("{message}", error.Message)
                                        .Replace("{details}", error.ToString())
                                        .Replace("{trace}", error.StackTrace);
                        RestOperationContext.Current.OutgoingResponse.ContentType = "text/html";
                        response.Body = new MemoryStream(Encoding.UTF8.GetBytes(errRsp));
                    }
                }

                AuditUtil.AuditNetworkRequestFailure(error, RestOperationContext.Current.IncomingRequest.Url, RestOperationContext.Current.IncomingRequest.Headers.AllKeys.ToDictionary(o => o, o => RestOperationContext.Current.IncomingRequest.Headers[o]), RestOperationContext.Current.OutgoingResponse.Headers.AllKeys.ToDictionary(o => o, o => RestOperationContext.Current.OutgoingResponse.Headers[o]));

                return(true);
            }
            catch (Exception e)
            {
                Tracer.GetTracer(typeof(AgsWebErrorHandlerServiceBehavior)).TraceError("Could not provide fault: {0}", e.ToString());
                throw;
            }
        }
        /// <summary>
        /// Apply the authorization policy rule
        /// </summary>
        public void Apply(RestRequestMessage request)
        {
            try
            {
                this.m_traceSource.TraceInfo("CheckAccess");

                // Http message inbound
                var httpMessage = RestOperationContext.Current.IncomingRequest;


                // Get the authorize header
                String authorization = httpMessage.Headers["Authorization"];
                if (authorization == null)
                {
                    if (httpMessage.HttpMethod == "OPTIONS" || httpMessage.HttpMethod == "PING")
                    {
                        return;
                    }
                    else
                    {
                        throw new SecuritySessionException(SessionExceptionType.NotEstablished, "Missing Authorization header", null);
                    }
                }

                // Authorization method
                var auth = authorization.Split(' ').Select(o => o.Trim()).ToArray();
                switch (auth[0].ToLowerInvariant())
                {
                case "bearer":
                    var contextToken = this.CheckBearerAccess(auth[1]);
                    RestOperationContext.Current.Disposed += (o, e) => contextToken.Dispose();
                    break;

                default:
                    throw new SecuritySessionException(SessionExceptionType.TokenType, "Invalid authentication scheme", null);
                }
            }
            catch (UnauthorizedAccessException e)
            {
                this.m_traceSource.TraceError("Token Error (From: {0}) : {1}", RestOperationContext.Current.IncomingRequest.RemoteEndPoint, e);
                AuditUtil.AuditNetworkRequestFailure(e, RestOperationContext.Current.IncomingRequest.Url, RestOperationContext.Current.IncomingRequest.Headers, null);
                throw;
            }
            catch (KeyNotFoundException e)
            {
                this.m_traceSource.TraceError("Token Error (From: {0}) : {1}", RestOperationContext.Current.IncomingRequest.RemoteEndPoint, e);
                AuditUtil.AuditNetworkRequestFailure(e, RestOperationContext.Current.IncomingRequest.Url, RestOperationContext.Current.IncomingRequest.Headers, null);
                throw new SecuritySessionException(SessionExceptionType.NotEstablished, e.Message, e);
            }
            catch (Exception e)
            {
                this.m_traceSource.TraceError("Token Error (From: {0}) : {1}", RestOperationContext.Current.IncomingRequest.RemoteEndPoint, e);
                AuditUtil.AuditNetworkRequestFailure(e, RestOperationContext.Current.IncomingRequest.Url, RestOperationContext.Current.IncomingRequest.Headers, null);
                throw new SecuritySessionException(SessionExceptionType.Other, e.Message, e);
            }
        }
        /// <summary>
        /// Provide fault
        /// </summary>
        public bool ProvideFault(Exception error, RestResponseMessage faultMessage)
        {
            var uriMatched = RestOperationContext.Current.IncomingRequest.Url;

            while (error.InnerException != null)
            {
                error = error.InnerException;
            }

            var fault      = new RestServiceFault(error);
            var authScheme = RestOperationContext.Current.AppliedPolicies.OfType <BasicAuthorizationAccessBehavior>().Any() ? "Basic" : "Bearer";
            var authRealm  = RestOperationContext.Current.IncomingRequest.Url.Host;

            // Formulate appropriate response
            if (error is DomainStateException)
            {
                faultMessage.StatusCode = (int)System.Net.HttpStatusCode.ServiceUnavailable;
            }
            else if (error is ObjectLockedException lockException)
            {
                faultMessage.StatusCode = 423;
                fault.Data.Add(lockException.LockedUser);
            }
            else if (error is PolicyViolationException)
            {
                var pve = error as PolicyViolationException;
                if (pve.PolicyDecision == PolicyGrantType.Elevate)
                {
                    // Ask the user to elevate themselves
                    faultMessage.StatusCode = 401;
                    faultMessage.AddAuthenticateHeader(authScheme, authRealm, "insufficient_scope", pve.PolicyId, error.Message);
                }
                else
                {
                    faultMessage.StatusCode = 403;
                }
            }
            else if (error is SecurityException)
            {
                faultMessage.StatusCode = (int)HttpStatusCode.Forbidden;
            }
            else if (error is SecurityTokenException)
            {
                // TODO: Audit this
                faultMessage.StatusCode = (int)System.Net.HttpStatusCode.Unauthorized;
                var authHeader = $"Bearer realm=\"{RestOperationContext.Current.IncomingRequest.Url.Host}\" error=\"invalid_token\" error_description=\"{error.Message}\"";
                faultMessage.AddAuthenticateHeader(authScheme, authRealm, error: "invalid_token", description: error.Message);
            }
            else if (error is LimitExceededException)
            {
                faultMessage.StatusCode        = (int)(HttpStatusCode)429;
                faultMessage.StatusDescription = "Too Many Requests";
                faultMessage.Headers.Add("Retry-After", "1200");
            }
            else if (error is AuthenticationException)
            {
                faultMessage.StatusCode = (int)System.Net.HttpStatusCode.Unauthorized;
                faultMessage.AddAuthenticateHeader(authScheme, authRealm, "invalid_token", description: error.Message);
            }
            else if (error is UnauthorizedAccessException)
            {
                faultMessage.StatusCode = (int)System.Net.HttpStatusCode.Forbidden;
            }
            else if (error is SecuritySessionException ses)
            {
                switch (ses.Type)
                {
                case SessionExceptionType.Expired:
                case SessionExceptionType.NotYetValid:
                case SessionExceptionType.NotEstablished:
                    faultMessage.StatusCode = (int)System.Net.HttpStatusCode.Unauthorized;
                    faultMessage.AddAuthenticateHeader(authScheme, authRealm, error: "unauthorized");
                    break;

                default:
                    faultMessage.StatusCode = (int)System.Net.HttpStatusCode.Forbidden;
                    break;
                }
            }
            else if (error is FaultException)
            {
                faultMessage.StatusCode = (int)(error as FaultException).StatusCode;
            }
            else if (error is Newtonsoft.Json.JsonException ||
                     error is System.Xml.XmlException)
            {
                faultMessage.StatusCode = (int)System.Net.HttpStatusCode.BadRequest;
            }
            else if (error is DuplicateKeyException || error is DuplicateNameException)
            {
                faultMessage.StatusCode = (int)System.Net.HttpStatusCode.Conflict;
            }
            else if (error is FileNotFoundException || error is KeyNotFoundException)
            {
                faultMessage.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
            }
            else if (error is DomainStateException)
            {
                faultMessage.StatusCode = (int)System.Net.HttpStatusCode.ServiceUnavailable;
            }
            else if (error is DetectedIssueException)
            {
                faultMessage.StatusCode = (int)(System.Net.HttpStatusCode) 422;
            }
            else if (error is NotImplementedException)
            {
                faultMessage.StatusCode = (int)HttpStatusCode.NotImplemented;
            }
            else if (error is NotSupportedException)
            {
                faultMessage.StatusCode = (int)HttpStatusCode.MethodNotAllowed;
            }
            else if (error is PatchException)
            {
                faultMessage.StatusCode = (int)HttpStatusCode.Conflict;
            }
            else
            {
                faultMessage.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
            }

            switch (faultMessage.StatusCode)
            {
            case 409:
            case 429:
            case 503:
                this.m_traceSource.TraceInfo("Issue on REST pipeline: {0}", error);
                break;

            case 401:
            case 403:
            case 501:
            case 405:
                this.m_traceSource.TraceWarning("Warning on REST pipeline: {0}", error);
                break;

            default:
                this.m_traceSource.TraceError("Error on REST pipeline: {0}", error);
                break;
            }

            RestMessageDispatchFormatter.CreateFormatter(RestOperationContext.Current.ServiceEndpoint.Description.Contract.Type).SerializeResponse(faultMessage, null, fault);
            AuditUtil.AuditNetworkRequestFailure(error, uriMatched, RestOperationContext.Current.IncomingRequest.Headers.AllKeys.ToDictionary(o => o, o => RestOperationContext.Current.IncomingRequest.Headers[o]), RestOperationContext.Current.OutgoingResponse.Headers.AllKeys.ToDictionary(o => o, o => RestOperationContext.Current.OutgoingResponse.Headers[o]));
            return(true);
        }
        /// <summary>
        /// Provide the fault
        /// </summary>
        /// <param name="error"></param>
        /// <param name="response"></param>
        /// <returns></returns>
        public bool ProvideFault(Exception error, RestResponseMessage faultMessage)
        {
            try
            {
#if DEBUG
                this.m_tracer.TraceWarning("Error on pipeline: {0}", error);
#else
                if (error is TargetInvocationException tie)
                {
                    this.m_tracer.TraceWarning("{0} - {1} / {2}", RestOperationContext.Current?.EndpointOperation?.Description?.InvokeMethod?.Name, error.Message, error.InnerException?.Message);
                }
                else
                {
                    this.m_tracer.TraceWarning("{0} - {1}", RestOperationContext.Current?.EndpointOperation?.Description?.InvokeMethod?.Name, error.Message);
                }
#endif

                if (faultMessage == null)
                {
                    if (RestOperationContext.Current.OutgoingResponse == null)
                    {
                        this.m_tracer.TraceWarning("Client hangup");
                        return(false);
                    }
                    this.m_tracer.TraceWarning("For some reason the fault message is null - ");
                    faultMessage = new RestResponseMessage(RestOperationContext.Current.OutgoingResponse);
                }
                var ie = error;
                while (ie != null)
                {
                    this.m_tracer.TraceWarning("{0} - ({1}){2} - {3}", error == ie ? "" : "Caused By",
                                               RestOperationContext.Current.EndpointOperation?.Description.InvokeMethod.Name,
                                               ie?.GetType().FullName, ie.Message);

                    // TODO: Do we need this or can we just capture the innermost exception as the cause?
                    if (ie is RestClientException <RestServiceFault> || ie is SecurityException || ie is DetectedIssueException ||
                        ie is FileNotFoundException || ie is KeyNotFoundException)
                    {
                        error = ie;
                    }
                    ie = ie.InnerException;
                }
                faultMessage.StatusCode = WebErrorUtility.ClassifyException(error);

                object fault = (error as RestClientException <RestServiceFault>)?.Result ?? new RestServiceFault(error);

                if (error is FaultException && error?.GetType() != typeof(FaultException)) // Special classification
                {
                    fault = error?.GetType().GetRuntimeProperty("Body").GetValue(error);
                }

                var formatter = RestMessageDispatchFormatter.CreateFormatter(RestOperationContext.Current.ServiceEndpoint.Description.Contract.Type);
                if (formatter != null)
                {
                    formatter.SerializeResponse(faultMessage, null, fault);
                }
                else
                {
                    RestOperationContext.Current.OutgoingResponse.OutputStream.Write(System.Text.Encoding.UTF8.GetBytes(error.Message), 0, System.Text.Encoding.UTF8.GetByteCount(error.Message));
                }

                try
                {
                    if (ApplicationServiceContext.Current.GetService <IOperatingSystemInfoService>()?.OperatingSystem != OperatingSystemID.Android)
                    {
                        AuditUtil.AuditNetworkRequestFailure(error, RestOperationContext.Current.IncomingRequest.Url, RestOperationContext.Current.IncomingRequest.Headers.AllKeys.ToDictionary(o => o, o => RestOperationContext.Current.IncomingRequest.Headers[o]), RestOperationContext.Current.OutgoingResponse.Headers.AllKeys.ToDictionary(o => o, o => RestOperationContext.Current.OutgoingResponse.Headers[o]));
                    }
                }
                catch (Exception e)
                {
                    this.m_tracer.TraceError("Could not send network request failure - {0}", e);
                }
            }
            catch (Exception e)
            {
                this.m_tracer.TraceError("Error providing fault: {0}", e);
            }
            return(true);
        }
Beispiel #5
0
        /// <summary>
        /// Receive and process message
        /// </summary>
        protected virtual void OnReceiveMessage(object client)
        {
            using (TcpClient tcpClient = client as TcpClient)
            {
                this.m_traceSource.TraceEvent(EventLevel.Verbose, "Accepted connection on {0} from {1}", this.m_listener.LocalEndpoint, tcpClient.Client.RemoteEndPoint);
                NetworkStream stream = tcpClient.GetStream();
                try
                {
                    // Now read to a string
                    DateTime lastReceive = DateTime.Now;

                    while (DateTime.Now.Subtract(lastReceive) < this.m_timeout)
                    {
                        if (!stream.DataAvailable)
                        {
                            Thread.Sleep(10);
                            continue;
                        }

                        // Read LLP head byte
                        int llpByte = stream.ReadByte();
                        if (llpByte != START_TX) // first byte must be HT
                        {
                            throw new InvalidOperationException("Invalid LLP First Byte");
                        }

                        // Standard stream stuff, read until the stream is exhausted
                        StringBuilder messageData = new StringBuilder();
                        byte[]        buffer = new byte[1024];
                        bool          receivedEOF = false, scanForCr = false;

                        while (!receivedEOF)
                        {
                            if (DateTime.Now.Subtract(lastReceive) > this.m_timeout)
                            {
                                throw new TimeoutException("Data not received in the specified amount of time. Increase the timeout or check the network connection");
                            }

                            if (!stream.DataAvailable)
                            {
                                Thread.Sleep(10);
                                continue;
                            }

                            int br = stream.Read(buffer, 0, 1024);
                            messageData.Append(System.Text.Encoding.UTF8.GetString(buffer, 0, br));

                            // Need to check for CR?
                            if (scanForCr)
                            {
                                receivedEOF = buffer[0] == END_TXNL;
                            }
                            else
                            {
                                // Look for FS
                                int fsPos = Array.IndexOf(buffer, (byte)END_TX);

                                if (fsPos == -1) // not found
                                {
                                    continue;
                                }
                                else if (fsPos < buffer.Length - 1) // more room to read
                                {
                                    receivedEOF = buffer[fsPos + 1] == END_TXNL;
                                }
                                else
                                {
                                    scanForCr = true; // Cannot check the end of message for CR because there is no more room in the message buffer
                                }
                                // so need to check on the next loop
                            }
                        }

                        // Use the nHAPI parser to process the data
                        Hl7MessageReceivedEventArgs messageArgs = null;
                        String originalVersion = null;

                        // Setup local and remote receive endpoint data for auditing
                        var localEp        = tcpClient.Client.LocalEndPoint as IPEndPoint;
                        var remoteEp       = tcpClient.Client.RemoteEndPoint as IPEndPoint;
                        Uri localEndpoint  = new Uri(String.Format("llp://{0}:{1}", localEp.Address, localEp.Port));
                        Uri remoteEndpoint = new Uri(String.Format("llp://{0}:{1}", remoteEp.Address, remoteEp.Port));

                        foreach (var messagePart in messageData.ToString().Split((char)END_TX))
                        {
                            if (messagePart == "\r")
                            {
                                continue;
                            }

                            try
                            {
                                this.m_traceSource.TraceInfo("Received message from llp://{0}:{1} : {2}", remoteEp.Address, remoteEp.Port, messagePart);
                                // HACK: nHAPI doesn't like URLs ... Will fix this later
                                string messageString = messagePart.Replace("|URL|", "|ST|");

                                var message = MessageUtils.ParseMessage(messageString, out originalVersion);
                                messageArgs = new Hl7MessageReceivedEventArgs(message, localEndpoint, remoteEndpoint, DateTime.Now);

                                HL7OperationContext.Current = new HL7OperationContext(messageArgs);

                                // Call any bound event handlers that there is a message available
                                OnMessageReceived(messageArgs);
                            }
                            catch (Exception e)
                            {
                                this.m_traceSource.TraceError("Error processing HL7 message: {0}", e);
                                if (messageArgs != null)
                                {
                                    var nack = new NHapi.Model.V25.Message.ACK();
                                    nack.MSH.SetDefault(messageArgs.Message.GetStructure("MSH") as NHapi.Model.V25.Segment.MSH);
                                    nack.MSA.AcknowledgmentCode.Value = "AE";
                                    nack.MSA.TextMessage.Value        = $"FATAL - {e.Message}";
                                    nack.MSA.MessageControlID.Value   = (messageArgs.Message.GetStructure("MSH") as NHapi.Model.V25.Segment.MSH).MessageControlID.Value;
                                    messageArgs.Response = nack;

                                    var icomps = PipeParser.Encode(messageArgs.Message.GetStructure("MSH") as NHapi.Base.Model.ISegment, new EncodingCharacters('|', "^~\\&")).Split('|');
                                    var ocomps = PipeParser.Encode(messageArgs.Response.GetStructure("MSH") as NHapi.Base.Model.ISegment, new EncodingCharacters('|', "^~\\&")).Split('|');
                                    AuditUtil.AuditNetworkRequestFailure(e, messageArgs.ReceiveEndpoint, Enumerable.Range(1, icomps.Length).ToDictionary(o => $"MSH-{o}", o => icomps[o - 1]), Enumerable.Range(1, icomps.Length).ToDictionary(o => $"MSH-{o}", o => ocomps[o - 1]));
                                }
                                else
                                {
                                    AuditUtil.AuditNetworkRequestFailure(e, localEndpoint, new System.Collections.Specialized.NameValueCollection(), new System.Collections.Specialized.NameValueCollection());
                                }
                            }
                            finally
                            {
                                // Send the response back
                                using (MemoryStream memoryWriter = new MemoryStream())
                                {
                                    using (StreamWriter streamWriter = new StreamWriter(memoryWriter))
                                    {
                                        memoryWriter.Write(new byte[] { START_TX }, 0, 1); // header
                                        if (messageArgs != null && messageArgs.Response != null)
                                        {
                                            var strMessage = MessageUtils.EncodeMessage(messageArgs.Response, originalVersion);
                                            this.m_traceSource.TraceInfo("Sending message to llp://{0} : {1}", tcpClient.Client.RemoteEndPoint, strMessage);
                                            // Since nHAPI only emits a string we just send that along the stream
                                            streamWriter.Write(strMessage);
                                            streamWriter.Flush();
                                        }
                                        memoryWriter.Write(new byte[] { END_TX, END_TXNL }, 0, 2); // Finish the stream with FSCR
                                        stream.Write(memoryWriter.ToArray(), 0, (int)memoryWriter.Position);
                                        stream.Flush();
                                    }
                                }
                                lastReceive = DateTime.Now; // Update the last receive time so the timeout function works
                            }
                        }

                        if (!stream.DataAvailable)
                        {
                            return;
                        }
                    }
                }
                catch (Exception e)
                {
                    this.m_traceSource.TraceEvent(EventLevel.Error, e.ToString());
                }
                finally
                {
                    stream.Close();
                    tcpClient.Close();
                    HL7OperationContext.Current = null;
                }
            }
        }
Beispiel #6
0
        /// <summary>
        /// Create a negative acknolwedgement from the specified exception
        /// </summary>
        /// <param name="request">The request message</param>
        /// <param name="error">The exception that occurred</param>
        /// <returns>NACK message</returns>
        protected virtual IMessage CreateNACK(Type nackType, IMessage request, Exception error, Hl7MessageReceivedEventArgs receiveData)
        {
            // Extract TIE into real cause
            while (error is TargetInvocationException)
            {
                error = error.InnerException;
            }

            IMessage retVal = null;

            if (error is DomainStateException)
            {
                retVal = this.CreateACK(nackType, request, "AR", "Domain Error");
            }
            else if (error is PolicyViolationException || error is SecurityException)
            {
                retVal = this.CreateACK(nackType, request, "AR", "Security Error");
            }
            else if (error is AuthenticationException || error is UnauthorizedAccessException)
            {
                retVal = this.CreateACK(nackType, request, "AR", "Unauthorized");
            }
            else if (error is Newtonsoft.Json.JsonException ||
                     error is System.Xml.XmlException)
            {
                retVal = this.CreateACK(nackType, request, "AR", "Messaging Error");
            }
            else if (error is DuplicateNameException)
            {
                retVal = this.CreateACK(nackType, request, "CR", "Duplicate Data");
            }
            else if (error is FileNotFoundException || error is KeyNotFoundException)
            {
                retVal = this.CreateACK(nackType, request, "CE", "Data not found");
            }
            else if (error is DetectedIssueException)
            {
                retVal = this.CreateACK(nackType, request, "CR", "Business Rule Violation");
            }
            else if (error is DataPersistenceException)
            {
                // Data persistence failed because of D/I/E
                if (error.InnerException is DetectedIssueException)
                {
                    error  = error.InnerException;
                    retVal = this.CreateACK(nackType, request, "CR", "Business Rule Violation");
                }
                else
                {
                    retVal = this.CreateACK(nackType, request, "CE", "Error committing data");
                }
            }
            else if (error is NotImplementedException)
            {
                retVal = this.CreateACK(nackType, request, "AE", "Not Implemented");
            }
            else if (error is NotSupportedException)
            {
                retVal = this.CreateACK(nackType, request, "AR", "Not Supported");
            }
            else if (error is HL7ProcessingException || error is HL7DatatypeProcessingException)
            {
                retVal = this.CreateACK(nackType, request, "AR", "Invalid Message");
            }
            else
            {
                retVal = this.CreateACK(nackType, request, "AE", "General Error");
            }


            var msa = retVal.GetStructure("MSA") as MSA;

            msa.ErrorCondition.Identifier.Value = this.MapErrCode(error);
            msa.ErrorCondition.Text.Value       = error.Message;

            int erc = 0;

            // Detected issue exception
            if (error is DetectedIssueException)
            {
                foreach (var itm in (error as DetectedIssueException).Issues)
                {
                    var err = retVal.GetStructure("ERR", erc) as ERR;
                    if (retVal.IsRepeating("ERR"))
                    {
                        erc++;
                    }

                    err.HL7ErrorCode.Identifier.Value = "207";
                    err.Severity.Value = itm.Priority == Core.BusinessRules.DetectedIssuePriorityType.Error ? "E" : itm.Priority == Core.BusinessRules.DetectedIssuePriorityType.Warning ? "W" : "I";
                    err.GetErrorCodeAndLocation(err.ErrorCodeAndLocationRepetitionsUsed).CodeIdentifyingError.Text.Value = itm.Text;
                }
            }
            else
            {
                var ex = error;
                while (ex != null)
                {
                    var err = retVal.GetStructure("ERR", erc) as ERR;
                    if (retVal.IsRepeating("ERR"))
                    {
                        erc++;
                    }

                    err.HL7ErrorCode.Identifier.Value = this.MapErrCode(ex);
                    err.Severity.Value = "E";
                    err.GetErrorCodeAndLocation(err.ErrorCodeAndLocationRepetitionsUsed).CodeIdentifyingError.Text.Value = ex.Message;
                    if (ex is HL7ProcessingException)
                    {
                        var hle = ex as HL7ProcessingException;
                        var erl = err.GetErrorLocation(err.ErrorLocationRepetitionsUsed);
                        erl.SegmentID.Value       = hle.Segment;
                        erl.SegmentSequence.Value = hle.Repetition ?? "1";
                        erl.FieldPosition.Value   = hle.Field.ToString();
                        erl.FieldRepetition.Value = "1";
                        erl.ComponentNumber.Value = hle.Component.ToString();

                        var ihle = (hle.InnerException as HL7DatatypeProcessingException)?.InnerException as HL7DatatypeProcessingException; // Nested DTE
                        if (ihle != null)
                        {
                            erl.SubComponentNumber.Value = ihle.Component.ToString();
                        }
                    }

                    ex = ex.InnerException;
                }
            }

            var icomps = PipeParser.Encode(request.GetStructure("MSH") as MSH, new EncodingCharacters('|', "^~\\&")).Split('|');
            var ocomps = PipeParser.Encode(retVal.GetStructure("MSH") as MSH, new EncodingCharacters('|', "^~\\&")).Split('|');

            AuditUtil.AuditNetworkRequestFailure(error, receiveData.ReceiveEndpoint,
                                                 Enumerable.Range(0, icomps.Length).ToDictionary(o => $"MSH-{o}", o => icomps[o]),
                                                 Enumerable.Range(0, ocomps.Length).ToDictionary(o => $"MSA-{o}", o => ocomps[o]));

            return(retVal);
        }