public void HandleSendable(Sendable sendable) { throwExceptionIfMissiongSetupCall(); if (sendable is Message message) { if (base.moduleType == message.TargetModuleType) { //handle request HandleRequest(message); } else { //forward the message ForwardSendable(message); } } else if (sendable is Response response) { if (base.moduleID == response.IntendedTargetModuleID) { //response for self //HandleResponse(response); throw new Exception("Do I need this?"); } else { ForwardSendable(response); } } }
/** Called by the Listener to process a received message. See if this * is a response and correlate with requests if it is, otherwise add * to the received messages queue. This is called by Listener.processMessage() * which is invoked in its own thread, so we don't need to spawn any new * threads here. * * @param Received message */ internal void receive(Sendable s) { if (s.SoapAction.Contains("service:Acknowledgment") || s.SoapAction.Contains("service:MessageError")) { // Asynchronous ack/nack. Remove from request list and exit. // EbXmlMessage m = (EbXmlMessage)s; if (!requests.Remove(m.Header.ConversationId)) { // Log receipt of ack/nack that doesn't belong to us... // Note: This may be legitimate in a clustered MHS or if in- and out-bound // nodes are separate. // EventLog logger = new EventLog("Application"); logger.Source = LOGSOURCE; StringBuilder sbe = new StringBuilder("Unexpected response "); sbe.Append(s.SoapAction); sbe.Append(" with conversation id "); sbe.Append(m.Header.ConversationId); sbe.Append(" that was not sent from here."); logger.WriteEntry(sbe.ToString(), EventLogEntryType.Information); } depersist(m.Header.ConversationId); return; } ISpineHandler h = null; try { h = handlers[s.SoapAction]; } catch (KeyNotFoundException) { EventLog logger = new EventLog("Application"); logger.Source = LOGSOURCE; StringBuilder sbe = new StringBuilder("Unknown SOAP action "); sbe.Append(s.SoapAction); sbe.Append(" using DefaultFileSaveSpineHandler"); logger.WriteEntry(sbe.ToString(), EventLogEntryType.FailureAudit); h = defaultSpineHandler; } try { h.handle(s); } catch (Exception e) { EventLog logger = new EventLog("Application"); logger.Source = LOGSOURCE; StringBuilder sbe = new StringBuilder("Exception handling "); sbe.Append(s.SoapAction); sbe.Append(" : "); sbe.Append(e.ToString()); logger.WriteEntry(sbe.ToString(), EventLogEntryType.FailureAudit); } }
protected void ForwardSendable(Sendable sendable) { throwExceptionIfMissiongSetupCall(); if (sendable is Message message) { this.moduleTypeToProxyHelper[message.TargetModuleType].ReciveSendable(sendable); } else if (sendable is Response response) { this.moduleIdToProxyHelper[response.IntendedTargetModuleID].ReciveSendable(sendable); } }
public async Task Process(BitmapDecoder decoder) { var bitmaps = await Split(decoder); bitmaps.AsParallel().ForAll(async k => { foreach (var light in Map.Blocks[k.Key].AffectedLights) { var color = await GetColorFromBitmap(k.Value); await Sendable.SendColorAsync(light.Id, color); } }); }
/** * Wrapper round the transmission and, where relevant retry and receipt of an asynchronous * response, for a "Sendable" message using the given transmission details resolved either * from SDS or the local SDS cache. This does the actual transmission in a separate thread * so will return immediately. * * @param Sendable s Message to send * @param SdsTransmissionDetails c retrieved transmission details for the message type/recipient pair */ public void send(Sendable s, SdsTransmissionDetails c) { if (!c.IsSynchronous) { listen(); // Don't expect an ack if we're sending an asynchronous ack, but otherwise do // do that we have something to attach a response to, if we're going to get one. // if (!(s.Type == Sendable.ACK) && (c.DuplicateElimination.Equals("always"))) { if (!requests.ContainsKey(s.getMessageId())) { requests.Add(s.getMessageId(), s); } } } (new Thread(() => this.doTransmission(s))).Start(); }
/** * Internal method to transmit a Sendable message, called by send() and by the retry mechanism. * This handles: * - de-queueing reliable messages that have been acknowledged synchronously (or for which an explicit error has been received), * - collecting any synchronous response which is stored in the SynchronousResponse property of the Sendable instance. * - calling the response handler for synchronous requests * - error logging * * Note that due to a design error in SDS not all target endpoint URLs are resolvable from SDS: * "forwarded" messages for which Spine is an intermediary must resolve the endpoint URL some * other way. See the SDSconnection API documentation for how this is configured. * * @param Message to transmit */ private void doTransmission(Sendable s) { // Create a socket and then do the TLS over it to // make an SSL stream. Then write the Sendable down the stream and get any // "stuff" back synchronously. If we do get a synchronous ack, remove the // reference to the transmitted message from "requests". // Socket clearSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // Note this uses the "ResolvedUrl" from the Sendable, not the "Url" from SDS because the // SDS data will be broken by design for "forwarded" services. // // TODO NEXT: Persist and send from persisted message string host = null; string syncresponse = null; if (s.ResolvedUrl == null) { // Sending persisted, reloaded message host = ((EbXmlMessage)s).getHost(); clearSocket.Connect(host, HTTPS); } else { // "Primary" transmission s.persist(); Uri uri = new Uri(s.ResolvedUrl); host = uri.Host; if (uri.Port == -1) { clearSocket.Connect(uri.Host, HTTPS); } else { clearSocket.Connect(host, uri.Port); } } if (!s.recordTry()) { if (s.getMessageId() != null) { removeRequest((EbXmlMessage)s); s.Expire(); } return; } string httpResponseLine = null; using (NetworkStream n = new NetworkStream(clearSocket)) { SslStream ssl = new SslStream(n, false, validateRemoteCertificate, new LocalCertificateSelectionCallback(getLocalCertificate)); //ssl.AuthenticateAsClient(host); ssl.AuthenticateAsClient(host, endpointCertificateCollection, SslProtocols.Tls, true); if (!ssl.IsAuthenticated || !ssl.IsSigned || !ssl.IsEncrypted) { EventLog logger = new EventLog("Application"); logger.Source = LOGSOURCE; logger.WriteEntry("Failed to authenticate SSL connection", EventLogEntryType.Error); throw new Exception("Failed to authenticate SSL connection"); } s.write(ssl); ssl.Flush(); // Read any response ... // ... first line first... httpResponseLine = readline(ssl); // ... then the rest of it int contentlength = getHeader(ssl); if (contentlength == -1) { EventLog logger = new EventLog("Application"); logger.Source = LOGSOURCE; logger.WriteEntry("Failed to read response content length", EventLogEntryType.Error); return; } // Process response and add it to the Sendable - Spine synchronous responses are all fairly // small, even for with-history retrievals if (contentlength > 0) { int read = 0; byte[] buffer = new byte[contentlength]; do { int r = ssl.Read(buffer, read, contentlength - read); if (r == -1) { EventLog logger = new EventLog("Application"); logger.Source = LOGSOURCE; logger.WriteEntry("Premature EOF sending " + s.getMessageId(), EventLogEntryType.Error); break; } read += r; } while (read < contentlength); syncresponse = Encoding.UTF8.GetString(buffer); } } s.SyncronousResponse = syncresponse; if (s.Type == Sendable.SOAP) { if (synchronousHandlers.ContainsKey(s.SoapAction)) { ISynchronousResponseHandler h = synchronousHandlers[s.SoapAction]; h.handle((SpineSOAPRequest)s); } else { defaultSynchronousResponseHandler.handle((SpineSOAPRequest)s); } return; } // "Don't listen for any response" conditions where we remove this message from the // current set of things-we're-waiting-on-responses-for are: // Explicit error (HTTP 500) // A synchronous ebXML response (Acknowledgement or MessageError) with our message id in it. // // Note: The contract properties in SDS are a mess, so don't bother trying to infer behaviour // from them. // if (s.getMessageId() != null) // Don't try for acks. { if (s.SyncronousResponse != null) { if (httpResponseLine.Contains("HTTP 5") || (s.SyncronousResponse.Contains(s.getMessageId()))) { removeRequest((EbXmlMessage)s); } } } }
/** * Internal method to transmit a Sendable message, called by send() and by the retry mechanism. * This handles: * - de-queueing reliable messages that have been acknowledged synchronously (or for which an explicit error has been received), * - collecting any synchronous response which is stored in the SynchronousResponse property of the Sendable instance. * - calling the response handler for synchronous requests * - error logging * * Note that due to a design error in SDS not all target endpoint URLs are resolvable from SDS: * "forwarded" messages for which Spine is an intermediary must resolve the endpoint URL some * other way. See the SDSconnection API documentation for how this is configured. * * @param Message to transmit */ private void doTransmission(Sendable s) { // Create a socket and then do the TLS over it to // make an SSL stream. Then write the Sendable down the stream and get any // "stuff" back synchronously. If we do get a synchronous ack, remove the // reference to the transmitted message from "requests". // Socket clearSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // Note this uses the "ResolvedUrl" from the Sendable, not the "Url" from SDS because the // SDS data will be broken by design for "forwarded" services. // // TODO NEXT: Persist and send from persisted message string host = null; string syncresponse = null; if (s.ResolvedUrl == null) { // Sending persisted, reloaded message host = ((EbXmlMessage)s).getHost(); clearSocket.Connect(host, HTTPS); } else { // "Primary" transmission s.persist(); Uri uri = new Uri(s.ResolvedUrl); host = uri.Host; if (uri.Port == -1) clearSocket.Connect(uri.Host, HTTPS); else clearSocket.Connect(host, uri.Port); } if (!s.recordTry()) { if (s.getMessageId() != null) { removeRequest((EbXmlMessage)s); s.Expire(); } return; } string httpResponseLine = null; using (NetworkStream n = new NetworkStream(clearSocket)) { SslStream ssl = new SslStream(n, false, validateRemoteCertificate, new LocalCertificateSelectionCallback(getLocalCertificate)); //ssl.AuthenticateAsClient(host); ssl.AuthenticateAsClient(host, endpointCertificateCollection, SslProtocols.Tls, true); if (!ssl.IsAuthenticated || !ssl.IsSigned || !ssl.IsEncrypted) { EventLog logger = new EventLog("Application"); logger.Source = LOGSOURCE; logger.WriteEntry("Failed to authenticate SSL connection", EventLogEntryType.Error); throw new Exception("Failed to authenticate SSL connection"); } s.write(ssl); ssl.Flush(); // Read any response ... // ... first line first... httpResponseLine = readline(ssl); // ... then the rest of it int contentlength = getHeader(ssl); if (contentlength == -1) { EventLog logger = new EventLog("Application"); logger.Source = LOGSOURCE; logger.WriteEntry("Failed to read response content length", EventLogEntryType.Error); return; } // Process response and add it to the Sendable - Spine synchronous responses are all fairly // small, even for with-history retrievals if (contentlength > 0) { int read = 0; byte[] buffer = new byte[contentlength]; do { int r = ssl.Read(buffer, read, contentlength - read); if (r == -1) { EventLog logger = new EventLog("Application"); logger.Source = LOGSOURCE; logger.WriteEntry("Premature EOF sending " + s.getMessageId(), EventLogEntryType.Error); break; } read += r; } while (read < contentlength); syncresponse = Encoding.UTF8.GetString(buffer); } } s.SyncronousResponse = syncresponse; if (s.Type == Sendable.SOAP) { if (synchronousHandlers.ContainsKey(s.SoapAction)) { ISynchronousResponseHandler h = synchronousHandlers[s.SoapAction]; h.handle((SpineSOAPRequest)s); } else { defaultSynchronousResponseHandler.handle((SpineSOAPRequest)s); } return; } // "Don't listen for any response" conditions where we remove this message from the // current set of things-we're-waiting-on-responses-for are: // Explicit error (HTTP 500) // A synchronous ebXML response (Acknowledgement or MessageError) with our message id in it. // // Note: The contract properties in SDS are a mess, so don't bother trying to infer behaviour // from them. // if (s.getMessageId() != null) // Don't try for acks. { if (s.SyncronousResponse != null) { if (httpResponseLine.Contains("HTTP 5") || (s.SyncronousResponse.Contains(s.getMessageId()))) removeRequest((EbXmlMessage)s); } } }
/** * Wrapper round the transmission and, where relevant retry and receipt of an asynchronous * response, for a "Sendable" message using the given transmission details resolved either * from SDS or the local SDS cache. This does the actual transmission in a separate thread * so will return immediately. * * @param Sendable s Message to send * @param SdsTransmissionDetails c retrieved transmission details for the message type/recipient pair */ public void send(Sendable s, SdsTransmissionDetails c) { if (!c.IsSynchronous) { listen(); // Don't expect an ack if we're sending an asynchronous ack, but otherwise do // do that we have something to attach a response to, if we're going to get one. // if (!(s.Type == Sendable.ACK) && (c.DuplicateElimination.Equals("always"))) { if (!requests.ContainsKey(s.getMessageId())) requests.Add(s.getMessageId(), s); } } (new Thread(() => this.doTransmission(s))).Start(); }