using System; using System.Collections.Generic; using Best.HTTP.Shared.PlatformSupport.Memory; using Best.MQTT.Packets; using Best.MQTT.Packets.Utils; namespace Best.MQTT { /// /// https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901106 /// public readonly struct ApplicationMessage { /// /// https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901108 /// internal readonly UInt16 PacketId; /// /// https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901117 /// internal readonly UInt32 SubscriptionId; /// /// Set to true if it's not the first occasion the broker sent this application message. /// /// public readonly bool IsDuplicate; /// /// QoS this application message sent with. /// /// public readonly QoSLevels QoS; /// /// Set to true if this is a retained application message. /// /// public readonly bool Retain; /// /// The topic's name this application message is publish to. /// /// public readonly string Topic; /// /// Payload type (binary or text). /// /// public readonly PayloadTypes PayloadFormat; /// /// Expiry interval of the application message. /// /// public readonly TimeSpan ExpiryInterval; /// /// Topic alias index the broker used. /// /// internal readonly UInt16 TopicAlias; /// /// Topic name where the publisher waiting for a response to this application message. /// /// public readonly string ResponseTopic; /// /// Arbitrary data sent with the application message. /// /// public readonly BufferSegment CorrelationData; /// /// Key-value pairs sent with the application message. /// /// public readonly List> UserProperties; /// /// Arbitrary value set by the publisher. /// /// public readonly string ContentType; /// /// Payload of the application message. /// /// public readonly BufferSegment Payload; internal ApplicationMessage(UInt32 sId, Packet packet) { // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901117 // Multiple Subscription Identifiers will be included if the publication is the result of a match to more than one subscription, in this case their order is not significant. this.SubscriptionId = sId; this.IsDuplicate = packet.Flags[3]; this.QoS = (QoSLevels)packet.Flags.Range(2, 1); this.Retain = packet.Flags[0]; this.Topic = packet.VariableHeaderFields[0].UTF8String.Key; // The Packet Identifier field is only present in PUBLISH packets where the QoS level is 1 or 2. this.PacketId = this.QoS >= QoSLevels.AtLeastOnceDelivery ? (UInt16)packet.VariableHeaderFields[1].Integer : (UInt16)0; Properties properties = packet.VariableHeaderFields.Properties; this.PayloadFormat = properties.TryFindData(PacketProperties.PayloadFormatIndicator, DataTypes.Byte, out var data) ? (PayloadTypes)data.Integer : PayloadTypes.Bytes; this.ExpiryInterval = properties.TryFindData(PacketProperties.MessageExpiryInterval, DataTypes.FourByteInteger, out data) ? TimeSpan.FromSeconds(data.Integer) : TimeSpan.MaxValue; this.TopicAlias = properties.TryFindData(PacketProperties.TopicAlias, DataTypes.TwoByteInteger, out data) ? (UInt16)data.Integer : (UInt16)0; this.ResponseTopic = properties.TryFindData(PacketProperties.ResponseTopic, DataTypes.UTF8String, out data) ? data.UTF8String.Key : null; this.CorrelationData = properties.TryFindData(PacketProperties.CorrelationData, DataTypes.Binary, out data) ? data.Binary : BufferSegment.Empty; this.UserProperties = properties.ConvertAll>(PacketProperties.UserProperty, kvp_data => kvp_data.UTF8String); this.ContentType = properties.TryFindData(PacketProperties.ContentType, DataTypes.UTF8String, out data) ? data.UTF8String.Key : null; this.Payload = packet.Payload.Count > 0 ? packet.Payload[0].Binary : BufferSegment.Empty; } } }