/** * Used to assemble an EbXmlMessage from the given Stream, which has been accepted from the network. * This expects to get the inbound HTTP stream (or at least an SslStream) from the start, because it * begins processing with the HTTP POST. */ public EbXmlMessage(Stream instream) { // Assemble from network - note that Spine messages are NOT chunked // string headerline = null; Dictionary <string, string> headers = new Dictionary <string, string>(); while ((headerline = readLine(instream)).Trim().Length != 0) { if (headerline.StartsWith("POST")) { int firstSpace = headerline.IndexOf(' '); int secondSpace = headerline.IndexOf(' ', firstSpace + 1); if ((firstSpace == -1) || (secondSpace == -1)) { throw new Exception("Malformed HTTP request line, can't parse POST context path"); } receivedContextPath = headerline.Substring(firstSpace, secondSpace - firstSpace); } else { int colon = headerline.IndexOf(":"); if (colon == -1) { throw new Exception("Malformed HTTP header - no field/data delimiter colon"); } headers.Add(headerline.Substring(0, colon).ToUpper(), headerline.Substring(colon + 1).Trim()); } } string ctype = headers["CONTENT-TYPE"]; if (ctype == null) { throw new Exception("Malformed HTTP headers - no Content-Type found"); } if (ctype.Contains("multipart/related")) { mimeboundary = setMimeBoundary(ctype); } receivedHost = headers["HOST"]; string clen = headers["CONTENT-LENGTH"]; if (clen == null) { throw new Exception("Malformed HTTP headers - no Content-Length found"); } int contentlength = Int32.Parse(clen); soapAction = headers["SOAPACTION"]; soapAction = soapAction.Replace('"', ' ').Trim(); // There is a bug in Spine-hosted services that turns a SOAPaction starting with // "urn:" into "urn:urn:" - fix this if we find it. // if (soapAction.StartsWith("urn:urn:")) { soapAction = soapAction.Substring(4); } // Read content-length bytes and parse out the various parts of the // received message. // TODO: Put in proper handling. // Byte[] wire = new Byte[contentlength]; int bytesRead = 0; while (bytesRead < contentlength) { bytesRead += instream.Read(wire, bytesRead, contentlength - bytesRead); } string msg = Encoding.UTF8.GetString(wire); // Split on the mimeboundary. "msg" doesn't contain the HTTP headers so we should // just be able to walk through the attachments. If we can't, report an exception // int startPosition = msg.IndexOf(mimeboundary, 0); if (startPosition == -1) { // Need to handle the case where the content is // actually an asynchronous ebXML ack. // // If content-type is text/xml and soapaction contains Acknowledgment or MessageError, // it is an ack/nack. If we get one of these we need to log it and tell the connection // manager about it. But we don't need to do any further processing. // if (ctype.ToLower().StartsWith("text/xml")) { if (soapAction.Contains("Acknowledgment")) { // Remove from requests, and exit string a = EbXmlAcknowledgment.getAckedMessageId(msg); if (a == null) { EventLog logger = new EventLog("Application"); logger.Source = ConnectionManager.LOGSOURCE; StringBuilder sb = new StringBuilder("Failed to extract message id reference from Acknowledgment: "); sb.Append(msg); logger.WriteEntry(sb.ToString(), EventLogEntryType.Error); return; } ConnectionManager cm = ConnectionManager.getInstance(); cm.registerAck(a); return; } if (soapAction.Contains("MessageError")) { // Remove from requests, and exit string a = EbXmlAcknowledgment.getAckedMessageId(msg); if (a == null) { EventLog logger = new EventLog("Application"); logger.Source = ConnectionManager.LOGSOURCE; StringBuilder sb = new StringBuilder("Failed to extract message id reference from MessageError: "); sb.Append(msg); logger.WriteEntry(sb.ToString(), EventLogEntryType.Error); return; } EventLog l = new EventLog("Application"); l.Source = ConnectionManager.LOGSOURCE; StringBuilder sbe = new StringBuilder("EbXML MessageError received: "); sbe.Append(msg); l.WriteEntry(sbe.ToString(), EventLogEntryType.Error); ConnectionManager cm = ConnectionManager.getInstance(); cm.registerAck(a); return; } } throw new Exception("Malformed message"); } int endPosition = 0; int partCount = 0; bool gotEndBoundary = false; do { startPosition += mimeboundary.Length; endPosition = msg.IndexOf(mimeboundary, startPosition); if (endPosition == -1) { gotEndBoundary = true; } else { switch (partCount) { case 0: header = new EbXmlHeader(msg.Substring(startPosition, endPosition - startPosition)); if (header.Timestamp != null) { started = DateTime.Parse(header.Timestamp); // We don't know how many attempts were actually made, so assume // only one try at the start time. // lastTry = DateTime.Parse(header.Timestamp); tries = 1; } break; case 1: hl7message = new SpineHL7Message(msg.Substring(startPosition, endPosition - startPosition)); break; default: if (attachments == null) { attachments = new List <Attachment>(); } // IMPROVEMENT: Make this more flexible to be able to support multiple types of // ITK trunk message, just in case // if (soapAction.Contains("COPC_IN000001GB01")) { attachments.Add(new ITKDistributionEnvelopeAttachment(msg.Substring(startPosition, endPosition - startPosition))); } else { attachments.Add(new GeneralAttachment(msg.Substring(startPosition, endPosition - startPosition))); } break; } partCount++; startPosition = endPosition; } }while (!gotEndBoundary); // persistDuration = (int)(ConnectionManager.getInstance().getPersistDuration(header.SvcIA).TotalSeconds); }
/** * Checks to see if the given EbXmlMessage is acknowledged asynchronously, and just returns * if not. Otherwise, constructs an EbXMLAcknowledgment addressed to the sender of "msg", * and calls the ConnectionManager to send it. * * @param EbXmlMessage to acknowledge */ private void doAsynchronousAck(EbXmlMessage msg) { if (msg == null) return; if (!msg.Header.DuplicateElimination) return; if (msg.Header.SyncReply) return; StringBuilder a = new StringBuilder(makeEbXmlAck(msg, false)); ConnectionManager cm = ConnectionManager.getInstance(); SDSconnection sds = cm.SdsConnection; string ods = msg.Header.FromPartyKey.Substring(0, msg.Header.FromPartyKey.IndexOf("-")); List<SdsTransmissionDetails> sdsdetails = sds.getTransmissionDetails(ACKSERVICE, ods, null, msg.Header.FromPartyKey); a.Replace("__CPA_ID__", sdsdetails[0].CPAid); EbXmlAcknowledgment ack = new EbXmlAcknowledgment(a.ToString()); ack.setHost(sdsdetails[0].Url); cm.send(ack, sdsdetails[0]); }