/// <summary> /// Receive and process message /// </summary> protected virtual void OnReceiveMessage(object client) { TcpClient tcpClient = client as TcpClient; 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; try { // 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)); #if DEBUG this.m_traceSource.TraceInfo("Received message from llp://{0}:{1} : {2}", remoteEp.Address, remoteEp.Port, messageData); #endif // HACK: nHAPI doesn't like URLs ... Will fix this later string messageString = messageData.ToString().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); } 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); #if DEBUG this.m_traceSource.TraceInfo("Sending message to llp://{0} : {1}", tcpClient.Client.RemoteEndPoint, strMessage); #endif // 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 } } } catch (Exception e) { this.m_traceSource.TraceEvent(EventLevel.Error, e.ToString()); } finally { stream.Close(); tcpClient.Close(); HL7OperationContext.Current = null; } }
/// <summary> /// Send a message and receive the message /// </summary> public IMessage SendAndReceive(IMessage message) { // Encode the message var strMessage = MessageUtils.EncodeMessage(message, (message.GetStructure("MSH") as NHapi.Model.V25.Segment.MSH).VersionID.VersionID.Value); #if DEBUG this.tracer.TraceEvent(EventLevel.Informational, strMessage); #endif // Open a TCP port using (var client = new TcpClient(AddressFamily.InterNetwork)) { try { // Connect on the socket client.Connect(this.endpoint.Host, this.endpoint.Port); // Get the stream using (var stream = client.GetStream()) { Stream realStream = stream; if (this.clientCertificate != null) { realStream = new SslStream(stream, false, this.RemoteCertificateValidation); var collection = new X509CertificateCollection { this.clientCertificate }; ((SslStream)realStream).AuthenticateAsClient(this.endpoint.ToString(), collection, System.Security.Authentication.SslProtocols.Tls, true); } // Write message in ASCII encoding byte[] buffer = Encoding.UTF8.GetBytes(strMessage); byte[] sendBuffer = new byte[buffer.Length + 3]; sendBuffer[0] = 0x0b; Array.Copy(buffer, 0, sendBuffer, 1, buffer.Length); Array.Copy(new byte[] { 0x1c, 0x0d }, 0, sendBuffer, sendBuffer.Length - 2, 2); stream.Write(sendBuffer, 0, sendBuffer.Length); // Write end message stream.Flush(); // Ensure all bytes get sent down the wire // Now read the response StringBuilder response = new StringBuilder(); buffer = new byte[1024]; while (!buffer.Contains((byte)0x1c)) // HACK: Keep reading until the buffer has the FS character { int br = stream.Read(buffer, 0, 1024); int ofs = 0; if (buffer[ofs] == '\v') { ofs = 1; br = br - 1; } response.Append(Encoding.UTF8.GetString(buffer, ofs, br)); } #if DEBUG this.tracer.TraceEvent(EventLevel.Informational, response.ToString()); #endif String version = null; return(MessageUtils.ParseMessage(response.ToString(), out version)); } } catch (Exception e) { #if DEBUG this.tracer.TraceEvent(EventLevel.Error, e.StackTrace); #endif this.tracer.TraceEvent(EventLevel.Error, e.Message); throw; } } }
/// <summary> /// Represent message as string /// </summary> public static String ToString(IMessage msg) { return(MessageUtils.EncodeMessage(msg, "2.5")); }
/// <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; } } }