public void EtpExtensions_CreateMessageKey_Creates_Core_Key() { long expectedKey, actualKey; expectedKey = 1L; actualKey = EtpExtensions.CreateMessageKey((int)v11.Protocols.Core, (int)v11.MessageTypes.Core.RequestSession); Assert.AreEqual(expectedKey, actualKey); expectedKey = 1L; actualKey = EtpExtensions.CreateMessageKey((int)v12.Protocols.Core, (int)v12.MessageTypes.Core.RequestSession); Assert.AreEqual(expectedKey, actualKey); expectedKey = 1000L; actualKey = EtpExtensions.CreateMessageKey((int)v11.Protocols.Core, (int)v11.MessageTypes.Core.ProtocolException); Assert.AreEqual(expectedKey, actualKey); expectedKey = 1000L; actualKey = EtpExtensions.CreateMessageKey((int)v12.Protocols.Core, (int)v12.MessageTypes.Core.ProtocolException); Assert.AreEqual(expectedKey, actualKey); expectedKey = 1001L; actualKey = EtpExtensions.CreateMessageKey((int)v11.Protocols.Core, (int)v11.MessageTypes.Core.Acknowledge); Assert.AreEqual(expectedKey, actualKey); expectedKey = 1001L; actualKey = EtpExtensions.CreateMessageKey((int)v12.Protocols.Core, (int)v12.MessageTypes.Core.Acknowledge); Assert.AreEqual(expectedKey, actualKey); }
/// <summary> /// Overrides an existing message decoder so that it can return a generic interface. /// </summary> /// <typeparam name="TInterface">The type of the message body interface.</typeparam> /// <typeparam name="TBody">The type of the decoded message.</typeparam> /// <param name="protocol"></param> /// <param name="messageType"></param> protected void RegisterMessageDecoderOverride <TInterface, TBody>(object protocol, object messageType) where TInterface : IEtpMessageBody where TBody : class, TInterface, new() { var messageKey = EtpExtensions.CreateMessageKey(Convert.ToInt32(protocol), Convert.ToInt32(messageType)); MessageDecoders[messageKey] = (d, h, x) => CreateMessage <TInterface, TBody>(h, d.DecodeAvroObject <TBody>(), extension: x); MessageDecodersByType[typeof(TInterface)] = MessageDecoders[messageKey]; }
public void EtpExtensions_CreateMessageKey_Creates_Discovery_Key() { long expectedKey, actualKey; expectedKey = (3L << 32) + 1; actualKey = EtpExtensions.CreateMessageKey((int)v11.Protocols.Discovery, (int)v11.MessageTypes.Discovery.GetResources); Assert.AreEqual(expectedKey, actualKey); expectedKey = (3L << 32) + 1; actualKey = EtpExtensions.CreateMessageKey((int)v12.Protocols.Discovery, (int)v12.MessageTypes.Discovery.GetResources); Assert.AreEqual(expectedKey, actualKey); expectedKey = 1000L; actualKey = EtpExtensions.CreateMessageKey((int)v11.Protocols.Discovery, (int)v11.MessageTypes.Core.ProtocolException); Assert.AreEqual(expectedKey, actualKey); expectedKey = 1000L; actualKey = EtpExtensions.CreateMessageKey((int)v12.Protocols.Discovery, (int)v12.MessageTypes.Core.ProtocolException); Assert.AreEqual(expectedKey, actualKey); expectedKey = 1001L; actualKey = EtpExtensions.CreateMessageKey((int)v11.Protocols.Discovery, (int)v11.MessageTypes.Core.Acknowledge); Assert.AreEqual(expectedKey, actualKey); expectedKey = 1001L; actualKey = EtpExtensions.CreateMessageKey((int)v12.Protocols.Discovery, (int)v12.MessageTypes.Core.Acknowledge); Assert.AreEqual(expectedKey, actualKey); }
/// <summary> /// Logs the specified message header and body. /// </summary> /// <typeparam name="T">The type of the message.</typeparam> /// <param name="header">The message header.</param> /// <param name="message">The message body.</param> protected void Received <T>(IMessageHeader header, T message) { if (Session?.Output == null) { return; } Session.Log("[{0}] Message received at {1}", Session.SessionId, DateTime.Now.ToString(TimestampFormat)); Session.Log(EtpExtensions.Serialize(header)); Session.Log(EtpExtensions.Serialize(message, true)); }
/// <summary> /// Handles the message. /// </summary> /// <param name="header">The header.</param> /// <param name="decoder">The decoder.</param> /// <param name="body">The body.</param> protected void HandleMessage(IMessageHeader header, Decoder decoder, string body) { try { IProtocolHandler handler; _handlersLock.TryEnterReadLock(-1); HandlersByProtocol.TryGetValue(header.Protocol, out handler); if (handler == null) { HandlersByProtocol.TryGetValue((int)v11.Protocols.Core, out handler); if (handler == null) // Socket has been closed { Logger.Trace($"Ignoring message on closed session: {EtpExtensions.Serialize(header)}"); return; } var msg = $"Protocol handler not registered for protocol { header.Protocol }."; handler.ProtocolException((int)EtpErrorCodes.UnsupportedProtocol, msg, header.MessageId); return; } var message = Adapter.DecodeMessage(header.Protocol, header.MessageType, decoder, body); if (message == null) { handler.InvalidMessage(header); return; } Received(header, message); try { // Handle global Acknowledge request if (header.IsAcknowledgeRequested()) { handler.Acknowledge(header.MessageId); } handler.HandleMessage(header, message); } catch (Exception ex) { Logger.Debug(ex); handler.ProtocolException((int)EtpErrorCodes.InvalidState, ex.Message, header.MessageId); } } finally { _handlersLock.ExitReadLock(); } }
/// <summary> /// Registers a function that decodes a particular message type. /// </summary> /// <typeparam name="T">The type of the decoded message.</typeparam> /// <param name="protocol">The message's protocol.</param> /// <param name="messageType">The type of the message.</param> /// <exception cref="ArgumentException">More than one decoder for the same protocol message is added.</exception> public void RegisterMessageDecoder <T>(object protocol, object messageType) where T : class, IEtpMessageBody, new() { var messageKey = EtpExtensions.CreateMessageKey(Convert.ToInt32(protocol), Convert.ToInt32(messageType)); if (MessageDecoders.ContainsKey(messageKey)) { throw new ArgumentException($"Duplicate decoder: Protocol: {protocol}; Message Type: {messageType}", "messageType"); } RegisterMessageDecoderOverride <T, T>(protocol, messageType); }
/// <summary> /// Registers a function that decodes a particular message type. /// </summary> /// <typeparam name="T">The type of the decoded message.</typeparam> /// <param name="protocol">The message's protocol.</param> /// <param name="messageType">The type of the message.</param> /// <exception cref="ArgumentException">More than one decoder for the same protocol message is added.</exception> public void RegisterMessageDecoder <T>(object protocol, object messageType) where T : ISpecificRecord { var messageKey = EtpExtensions.CreateMessageKey(Convert.ToInt32(protocol), Convert.ToInt32(messageType)); if (MessageDecoders.ContainsKey(messageKey)) { throw new ArgumentException($"Duplicate decoder: Protocol: {protocol}; Message Type: {messageType}", "messageType"); } MessageDecoders[messageKey] = (d, b) => EtpExtensions.Decode <T>(d, b); MessageDecodersByType[typeof(T)] = MessageDecoders[messageKey]; }
/// <summary> /// Decodes the message. /// </summary> /// <param name="header">The message header</param> /// <param name="extension">The header extension</param> /// <param name="decoder">The decoder with binary message data.</param> /// <returns>The message if successful; <c>null</c> otherwise.</returns> public EtpMessage DecodeMessage(IMessageHeader header, IMessageHeaderExtension extension, IAvroDecoder decoder) { var messageKey = EtpExtensions.CreateMessageKey(header.Protocol, header.MessageType); Func <IAvroDecoder, IMessageHeader, IMessageHeaderExtension, EtpMessage> messageDecoder; if (!MessageDecoders.TryGetValue(messageKey, out messageDecoder)) { return(null); } return(messageDecoder(decoder, header, extension)); }
/// <summary> /// Decodes the message type from the specified protocol. /// </summary> /// <param name="protocol">The message's protocol.</param> /// <param name="messageType">The type of the message.</param> /// <param name="decoder">The decoder with binary message data.</param> /// <param name="body">The string with json message data.</param> /// <returns>The message.</returns> public ISpecificRecord DecodeMessage(int protocol, int messageType, Decoder decoder, string body) { var messageKey = EtpExtensions.CreateMessageKey(protocol, messageType); Func <Decoder, string, ISpecificRecord> messageDecoder; if (!MessageDecoders.TryGetValue(messageKey, out messageDecoder)) { return(null); } return(messageDecoder(decoder, body)); }
/// <summary> /// Asynchronously sends the message. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="header">The header.</param> /// <param name="body">The body.</param> /// <param name="onBeforeSend">Action called just before sending the message with the actual header having the definitive message ID.</param> /// <returns>The message identifier.</returns> public async Task <long> SendMessageAsync <T>(IMessageHeader header, T body, Action <IMessageHeader> onBeforeSend = null) where T : ISpecificRecord { // Lock to ensure only one thread at a time attempts to send data and to ensure that messages are sent with sequential IDs try { try { await _sendLock.WaitAsync().ConfigureAwait(CaptureAsyncContext); if (!IsOpen) { Log("Warning: Sending on a session that is not open."); Logger.Debug("Sending on a session that is not open."); return(-1); } header.MessageId = NewMessageId(); // Call the pre-send action in case any deterministic handling is needed with the actual message ID. // Must be invoked before sending to ensure the response is not asynchronously processed before this method returns. onBeforeSend?.Invoke(header); // Log message just before it gets sent if needed. Sending(header, body); if (IsJsonEncoding) { var message = EtpExtensions.Serialize(new object[] { header, body }); await SendAsync(message).ConfigureAwait(CaptureAsyncContext); } else { var data = body.Encode(header, SupportedCompression); await SendAsync(data, 0, data.Length).ConfigureAwait(CaptureAsyncContext); } } finally { _sendLock.Release(); } } catch (Exception ex) { // Handler already locked by the calling code... return(Handler(header.Protocol) .ProtocolException((int)EtpErrorCodes.InvalidState, ex.Message, header.MessageId)); } return(header.MessageId); }
/// <summary> /// Logs the specified message header and body. /// </summary> /// <param name="header">The message header.</param> /// <param name="message">The message body.</param> protected void Received(IMessageHeader header, ISpecificRecord message) { var now = DateTime.Now; if (Output != null) { Log("[{0}] Message received at {1}", SessionId, now.ToString(TimestampFormat)); Log(EtpExtensions.Serialize(header)); Log(EtpExtensions.Serialize(message, true)); } if (Logger.IsVerboseEnabled()) { Logger.VerboseFormat("[{0}] Message received at {1}: {2}{3}{4}", SessionId, now.ToString(TimestampFormat), EtpExtensions.Serialize(header), Environment.NewLine, EtpExtensions.Serialize(message, true)); } }
/// <summary> /// Logs the specified header and message body. /// </summary> /// <typeparam name="T">The type of message.</typeparam> /// <param name="header">The header.</param> /// <param name="body">The message body.</param> protected void Sending <T>(IMessageHeader header, T body) { var now = DateTime.Now; if (Output != null) { Log("[{0}] Sending message at {1}", SessionId, now.ToString(TimestampFormat)); Log(EtpExtensions.Serialize(header)); Log(EtpExtensions.Serialize(body, true)); } if (Logger.IsVerboseEnabled()) { Logger.VerboseFormat("[{0}] Sending message at {1}: {2}{3}{4}", SessionId, now.ToString(TimestampFormat), EtpExtensions.Serialize(header), Environment.NewLine, EtpExtensions.Serialize(body, true)); } }
/// <summary> /// Invokes the message handler for the specified message type. /// </summary> /// <param name="header">The message header.</param> /// <param name="body">The message body.</param> /// <returns><c>true</c> if a handler for the message was invoked; <c>false</c> otherwise.</returns> public bool InvokeMessageHandler(IMessageHeader header, ISpecificRecord body) { if (header == null) { return(false); } var messageKey = EtpExtensions.CreateMessageKey(header.Protocol, header.MessageType); Action <IMessageHeader, ISpecificRecord> messageHandler; if (!MessageHandlers.TryGetValue(messageKey, out messageHandler)) { return(false); } messageHandler(header, body); return(true); }
public void EtpExtensions_Encode_And_Decode_Can_Round_Trip() { var expectedVersion = new v11.Datatypes.Version { Major = 1, Minor = 2, Revision = 3, Patch = 4 }; var bytes = expectedVersion.EncodeToBytes(); var actualVersion = EtpExtensions.DecodeFromBytes <v11.Datatypes.Version>(bytes); Assert.AreEqual(expectedVersion.Major, actualVersion.Major, nameof(expectedVersion.Major)); Assert.AreEqual(expectedVersion.Minor, actualVersion.Minor, nameof(expectedVersion.Minor)); Assert.AreEqual(expectedVersion.Revision, actualVersion.Revision, nameof(expectedVersion.Revision)); Assert.AreEqual(expectedVersion.Patch, actualVersion.Patch, nameof(expectedVersion.Patch)); var expectedValue = new v11.Datatypes.DataValue { Item = 1 }; bytes = expectedValue.EncodeToBytes(); var actualValue = EtpExtensions.DecodeFromBytes <v11.Datatypes.DataValue>(bytes); Assert.AreEqual(expectedValue.Item, actualValue.Item, nameof(expectedValue.Item)); expectedValue = new v11.Datatypes.DataValue { Item = 1.2 }; bytes = expectedValue.EncodeToBytes(); actualValue = EtpExtensions.DecodeFromBytes <v11.Datatypes.DataValue>(bytes); Assert.AreEqual(expectedValue.Item, actualValue.Item, nameof(expectedValue.Item)); expectedValue = new v11.Datatypes.DataValue { Item = "test" }; bytes = expectedValue.EncodeToBytes(); actualValue = EtpExtensions.DecodeFromBytes <v11.Datatypes.DataValue>(bytes); Assert.AreEqual(expectedValue.Item, actualValue.Item, nameof(expectedValue.Item)); var expectedProtocol = new v11.Datatypes.SupportedProtocol { Protocol = 1, ProtocolVersion = expectedVersion, ProtocolCapabilities = new Dictionary <string, v11.Datatypes.DataValue> { ["test"] = expectedValue }, Role = "store" }; bytes = expectedProtocol.EncodeToBytes(); var actualProtocol = EtpExtensions.DecodeFromBytes <v11.Datatypes.SupportedProtocol>(bytes); Assert.AreEqual(expectedProtocol.Protocol, actualProtocol.Protocol, nameof(expectedProtocol.Protocol)); Assert.AreEqual(expectedProtocol.ProtocolVersion.Major, actualProtocol.ProtocolVersion.Major, nameof(expectedProtocol.ProtocolVersion.Major)); Assert.AreEqual(expectedProtocol.ProtocolVersion.Minor, actualProtocol.ProtocolVersion.Minor, nameof(expectedProtocol.ProtocolVersion.Minor)); Assert.AreEqual(expectedProtocol.ProtocolVersion.Revision, actualProtocol.ProtocolVersion.Revision, nameof(expectedProtocol.ProtocolVersion.Revision)); Assert.AreEqual(expectedProtocol.ProtocolVersion.Patch, actualProtocol.ProtocolVersion.Patch, nameof(expectedProtocol.ProtocolVersion.Patch)); Assert.AreEqual(expectedProtocol.Role, actualProtocol.Role, nameof(expectedProtocol.Role)); Assert.AreEqual(1, actualProtocol.ProtocolCapabilities.Count, nameof(expectedProtocol.ProtocolCapabilities.Count)); Assert.AreEqual(expectedValue.Item, actualProtocol.ProtocolCapabilities["test"].Item); var expectedRequestSession = new v11.Protocol.Core.RequestSession { ApplicationName = "Application", ApplicationVersion = "Version", RequestedProtocols = new List <v11.Datatypes.SupportedProtocol> { expectedProtocol }, SupportedObjects = new List <string> { "object" }, }; bytes = expectedRequestSession.EncodeToBytes(); var actualRequestSession = EtpExtensions.DecodeFromBytes <v11.Protocol.Core.RequestSession>(bytes); Assert.AreEqual(expectedRequestSession.ApplicationName, actualRequestSession.ApplicationName, nameof(expectedRequestSession.ApplicationName)); Assert.AreEqual(expectedRequestSession.ApplicationVersion, actualRequestSession.ApplicationVersion, nameof(expectedRequestSession.ApplicationVersion)); Assert.AreEqual(expectedRequestSession.RequestedProtocols.Count, actualRequestSession.RequestedProtocols.Count, nameof(expectedRequestSession.RequestedProtocols.Count)); Assert.AreEqual(expectedRequestSession.RequestedProtocols[0].Protocol, actualRequestSession.RequestedProtocols[0].Protocol); Assert.AreEqual(expectedRequestSession.RequestedProtocols[0].ProtocolVersion.Major, actualRequestSession.RequestedProtocols[0].ProtocolVersion.Major); Assert.AreEqual(expectedRequestSession.RequestedProtocols[0].ProtocolVersion.Minor, actualRequestSession.RequestedProtocols[0].ProtocolVersion.Minor); Assert.AreEqual(expectedRequestSession.RequestedProtocols[0].ProtocolVersion.Revision, actualRequestSession.RequestedProtocols[0].ProtocolVersion.Revision); Assert.AreEqual(expectedRequestSession.RequestedProtocols[0].ProtocolVersion.Patch, actualRequestSession.RequestedProtocols[0].ProtocolVersion.Patch); Assert.AreEqual(expectedRequestSession.RequestedProtocols[0].Role, actualRequestSession.RequestedProtocols[0].Role); Assert.AreEqual(1, actualRequestSession.RequestedProtocols[0].ProtocolCapabilities.Count); Assert.AreEqual(expectedValue.Item, actualProtocol.ProtocolCapabilities["test"].Item); Assert.AreEqual(expectedRequestSession.SupportedObjects.Count, actualRequestSession.SupportedObjects.Count, nameof(expectedRequestSession.SupportedObjects.Count)); Assert.AreEqual(expectedRequestSession.SupportedObjects[0], actualRequestSession.SupportedObjects[0]); var expectedHeader = new v11.Datatypes.MessageHeader { MessageFlags = 0, Protocol = 1, MessageId = 2, CorrelationId = 3, MessageType = 4 }; var expectedMessage = new EtpMessage <v11.Protocol.Core.RequestSession>(expectedHeader, expectedRequestSession); bytes = expectedMessage.Encode(); using (var inputStream = new MemoryStream(bytes)) using (var decoder = new BinaryAvroDecoder(inputStream)) { var actualHeader = decoder.DecodeAvroObject <v11.Datatypes.MessageHeader>(); var actualBody = decoder.DecodeAvroObject <v11.Protocol.Core.RequestSession>(); Assert.AreEqual(expectedHeader.MessageFlags, actualHeader.MessageFlags, nameof(expectedHeader.MessageFlags)); Assert.AreEqual(expectedHeader.Protocol, actualHeader.Protocol, nameof(expectedHeader.Protocol)); Assert.AreEqual(expectedHeader.MessageId, actualHeader.MessageId, nameof(expectedHeader.MessageId)); Assert.AreEqual(expectedHeader.CorrelationId, actualHeader.CorrelationId, nameof(expectedHeader.CorrelationId)); Assert.AreEqual(expectedHeader.MessageType, actualHeader.MessageType, nameof(expectedHeader.MessageType)); Assert.AreEqual(expectedRequestSession.ApplicationName, actualBody.ApplicationName, nameof(expectedRequestSession.ApplicationName)); Assert.AreEqual(expectedRequestSession.ApplicationVersion, actualBody.ApplicationVersion, nameof(expectedRequestSession.ApplicationVersion)); Assert.AreEqual(expectedRequestSession.RequestedProtocols.Count, actualBody.RequestedProtocols.Count, nameof(expectedRequestSession.RequestedProtocols.Count)); Assert.AreEqual(expectedRequestSession.RequestedProtocols[0].Protocol, actualBody.RequestedProtocols[0].Protocol); Assert.AreEqual(expectedRequestSession.RequestedProtocols[0].ProtocolVersion.Major, actualBody.RequestedProtocols[0].ProtocolVersion.Major); Assert.AreEqual(expectedRequestSession.RequestedProtocols[0].ProtocolVersion.Minor, actualBody.RequestedProtocols[0].ProtocolVersion.Minor); Assert.AreEqual(expectedRequestSession.RequestedProtocols[0].ProtocolVersion.Revision, actualBody.RequestedProtocols[0].ProtocolVersion.Revision); Assert.AreEqual(expectedRequestSession.RequestedProtocols[0].ProtocolVersion.Patch, actualBody.RequestedProtocols[0].ProtocolVersion.Patch); Assert.AreEqual(expectedRequestSession.RequestedProtocols[0].Role, actualBody.RequestedProtocols[0].Role); Assert.AreEqual(1, actualBody.RequestedProtocols[0].ProtocolCapabilities.Count); Assert.AreEqual(expectedValue.Item, actualProtocol.ProtocolCapabilities["test"].Item); Assert.AreEqual(expectedRequestSession.SupportedObjects.Count, actualBody.SupportedObjects.Count, nameof(expectedRequestSession.SupportedObjects.Count)); Assert.AreEqual(expectedRequestSession.SupportedObjects[0], actualBody.SupportedObjects[0]); } }
/// <summary> /// Decodes the specified data. /// </summary> /// <param name="data">The data.</param> protected void Decode(byte[] data) { using (var inputStream = new MemoryStream(data)) { // create avro binary decoder to read from memory stream var decoder = new BinaryDecoder(inputStream); // deserialize the header var header = Adapter.DecodeMessageHeader(decoder, null); // log message metadata if (Logger.IsVerboseEnabled()) { Logger.VerboseFormat("[{0}] Binary message received: {1}", SessionId, EtpExtensions.Serialize(header)); } Stream gzip = null; try { // decompress message body if compression has been negotiated if (header.CanCompressMessageBody(true)) { if (EtpExtensions.GzipEncoding.Equals(SupportedCompression, StringComparison.InvariantCultureIgnoreCase)) { gzip = new GZipStream(inputStream, CompressionMode.Decompress, true); decoder = new BinaryDecoder(gzip); } } // call processing action HandleMessage(header, decoder, null); } finally { gzip?.Dispose(); } } }
/// <summary> /// Checks whether a decoder is registered for the specified protocol message. /// </summary> /// <param name="protocol">The message protocol.</param> /// <param name="messageType">The message type.</param> /// <returns><c>true</c> if there is a message decoder registered; <c>false</c> otherwise.</returns> public bool IsMessageDecoderRegistered(object protocol, object messageType) { var messageKey = EtpExtensions.CreateMessageKey(Convert.ToInt32(protocol), Convert.ToInt32(messageType)); return(MessageDecoders.ContainsKey(messageKey)); }
/// <summary> /// Registers a handler for the specific protocol message. /// </summary> /// <typeparam name="T">The message type.</typeparam> /// <param name="protocol">The message protocol.</param> /// <param name="messageType">The type of the message.</param> /// <param name="messageHandler">The protocol message handler.</param> /// <remarks>If more than one handler is registered for the same protocol message, the last registered handler will be used.</remarks> public void RegisterMessageHandler <T>(object protocol, object messageType, Action <IMessageHeader, T> messageHandler) where T : ISpecificRecord { var messageKey = EtpExtensions.CreateMessageKey(Convert.ToInt32(protocol), Convert.ToInt32(messageType)); MessageHandlers[messageKey] = (h, b) => messageHandler(h, (T)b); }