public static async Task<PublishPacket> ComposePublishPacketAsync(IChannelHandlerContext context, Message message,
            QualityOfService qos, string topicName)
        {
            bool duplicate = message.DeliveryCount > 0;

            var packet = new PublishPacket(qos, duplicate, false);
            packet.TopicName = topicName;
            if (qos > QualityOfService.AtMostOnce)
            {
                int packetId = unchecked((ushort)message.SequenceNumber);
                switch (qos)
                {
                    case QualityOfService.AtLeastOnce:
                        packetId &= 0x7FFF; // clear 15th bit
                        break;
                    case QualityOfService.ExactlyOnce:
                        packetId |= 0x8000; // set 15th bit
                        break;
                    default:
                        throw new ArgumentOutOfRangeException("qos", qos, null);
                }
                packet.PacketId = packetId;
            }
            using (Stream payloadStream = message.GetBodyStream())
            {
                long streamLength = payloadStream.Length;
                if (streamLength > int.MaxValue)
                {
                    throw new InvalidOperationException(string.Format("Message size ({0} bytes) is too big to process.", streamLength));
                }

                int length = (int)streamLength;
                IByteBuffer buffer = context.Channel.Allocator.Buffer(length, length);
                await buffer.WriteBytesAsync(payloadStream, length);
                Contract.Assert(buffer.ReadableBytes == length);

                packet.Payload = buffer;
            }
            return packet;
        }
Exemple #2
0
 IByteBufferHolder IByteBufferHolder.Duplicate()
 {
     var result = new PublishPacket(this.qos, this.duplicate, this.retainRequested);
     result.TopicName = this.TopicName;
     result.Payload = this.Payload.Duplicate();
     return result;
 }
        public IByteBufferHolder Replace(IByteBuffer content)
        {
            var result = new PublishPacket(this.qos, this.duplicate, this.retainRequested);

            result.TopicName = this.TopicName;
            result.Payload   = content;
            return(result);
        }
Exemple #4
0
        IByteBufferHolder IByteBufferHolder.Duplicate()
        {
            var result = new PublishPacket(this.qos, this.duplicate, this.retainRequested);

            result.TopicName = this.TopicName;
            result.Payload   = this.Payload.Duplicate();
            return(result);
        }
 public AckPendingMessageState(MessageWithFeedback messageWithFeedback, PublishPacket packet)
 {
     this.SequenceNumber = messageWithFeedback.Message.SequenceNumber;
     this.PacketId = packet.PacketId;
     this.QualityOfService = packet.QualityOfService;
     this.FeedbackChannel = messageWithFeedback.FeedbackChannel;
     this.SentTime = DateTime.UtcNow;
     this.StartTimestamp = PreciseTimeSpan.FromStart;
 }
        Task AcceptMessageAsync(IChannelHandlerContext context, PublishPacket publish)
        {
            Message message;
            try
            {
                var bodyStream = new ReadOnlyByteBufferStream(publish.Payload, true);

                message = new Message(bodyStream, true);

                Util.PopulateMessagePropertiesFromPacket(message, publish);
            }
            catch (Exception ex)
            {
                ShutdownOnError(context, ex);
                return TaskConstants.Completed;
            }
            this.onMessageReceived(message);
            return TaskConstants.Completed;
        }
 public AckPendingMessageState(Message message, PublishPacket packet)
     : this(message.MessageId, packet.PacketId, packet.QualityOfService, message.LockToken)
 {
 }
        void ProcessPublish(IChannelHandlerContext context, PublishPacket packet)
        {
            if (this.IsInState(StateFlags.Closed))
            {
                return;
            }

            switch (packet.QualityOfService)
            {
                case QualityOfService.AtMostOnce:
                    this.deviceBoundOneWayProcessor.Post(context, packet);
                    break;
                case QualityOfService.AtLeastOnce:
                    this.deviceBoundTwoWayProcessor.Post(context, packet);
                    break;
                default:
                    throw new NotSupportedException($"Unexpected QoS: '{packet.QualityOfService}'");
            }
            this.ResumeReadingIfNecessary(context);
        }
        async Task PublishToClientQos2Async(IChannelHandlerContext context, Message message, PublishPacket packet)
        {
            int packetId = packet.PacketId;
            IQos2MessageDeliveryState messageInfo = await this.qos2StateProvider.GetMessageAsync(packetId);

            if (messageInfo != null && !message.MessageId.Equals(messageInfo.MessageId, StringComparison.Ordinal))
            {
                await this.qos2StateProvider.DeleteMessageAsync(packetId, messageInfo);
                messageInfo = null;
            }

            if (messageInfo == null)
            {
                await this.publishPubRecProcessor.SendRequestAsync(context, packet, new AckPendingMessageState(message, packet));
            }
            else
            {
                await this.PublishReleaseToClientAsync(context, packetId, message.LockToken, messageInfo, PreciseTimeSpan.FromStart);
            }
        }
 Task PublishToClientQos0Async(IChannelHandlerContext context, Message message, PublishPacket packet)
 {
     if (message.DeliveryCount == 0)
     {
         return Task.WhenAll(
             this.iotHubClient.CompleteAsync(message.LockToken),
             Util.WriteMessageAsync(context, packet));
     }
     else
     {
         return this.iotHubClient.CompleteAsync(message.LockToken);
     }
 }
 Task PublishToServerAsync(IChannelHandlerContext context, PublishPacket packet)
 {
     return this.PublishToServerAsync(context, packet, null);
 }
        async Task CompletePublishAsync(IChannelHandlerContext context, PublishPacket will)
        {
            await this.publishProcessor.Completion;

            if (will == null)
            {
                return;
            }

            try
            {
                await this.PublishToServerAsync(context, will, MessageTypes.Will);
            }
            catch (Exception ex)
            {
                MqttIotHubAdapterEventSource.Log.Warning("Failed sending Will Message.", ex);
            }
        }
        async void CloseIotHubConnection(IChannelHandlerContext context, PublishPacket will)
        {
            if (!this.ConnectedToHub)
            {
                // closure happened before IoT Hub connection was established or it was initiated due to disconnect
                return;
            }

            try
            {
                this.publishProcessor.Complete();
                this.publishPubAckProcessor.Complete();
                this.publishPubRecProcessor.Complete();
                this.pubRelPubCompProcessor.Complete();
                await Task.WhenAll(
                    this.CompletePublishAsync(context, will),
                    this.publishPubAckProcessor.Completion,
                    this.publishPubRecProcessor.Completion,
                    this.pubRelPubCompProcessor.Completion);

                IMessagingServiceClient hub = this.messagingServiceClient;
                this.messagingServiceClient = null;
                await hub.DisposeAsync();
            }
            catch (Exception ex)
            {
                MqttIotHubAdapterEventSource.Log.Info("Failed to close IoT Hub Client cleanly.", ex.ToString());
            }
        }
        public static PublishPacket ComposePublishPacket(IChannelHandlerContext context, IMessage message,
            QualityOfService qos, IByteBufferAllocator allocator)
        {
            bool duplicate = message.DeliveryCount > 0;

            var packet = new PublishPacket(qos, duplicate, false);
            packet.TopicName = message.Address;
            if (qos > QualityOfService.AtMostOnce)
            {
                int packetId = unchecked((int)message.SequenceNumber) & 0x3FFF; // clear bits #14 and #15
                switch (qos)
                {
                    case QualityOfService.AtLeastOnce:
                        break;
                    case QualityOfService.ExactlyOnce:
                        packetId |= 0x8000; // set bit #15
                        break;
                    default:
                        throw new ArgumentOutOfRangeException(nameof(qos), qos, null);
                }
                packet.PacketId = packetId + 1;
            }
            message.Payload.Retain();
            packet.Payload = message.Payload;
            return packet;
        }
        public static IMessage CompleteMessageFromPacket(IMessage message, PublishPacket packet, Settings settings)
        {
            if (packet.RetainRequested)
            {
                message.Properties[settings.RetainPropertyName] = IotHubTrueString;
            }
            if (packet.Duplicate)
            {
                message.Properties[settings.DupPropertyName] = IotHubTrueString;
            }

            return message;
        }
        async Task PublishToServerAsync(IChannelHandlerContext context, PublishPacket packet)
        {
            if (!this.ConnectedToHub)
            {
                return;
            }

            PreciseTimeSpan startedTimestamp = PreciseTimeSpan.FromStart;

            this.ResumeReadingIfNecessary(context);

            using (Stream bodyStream = packet.Payload.IsReadable() ? new ReadOnlyByteBufferStream(packet.Payload, true) : null)
            {
                var message = new Message(bodyStream);
                this.ApplyRoutingConfiguration(message, packet);

                Util.CompleteMessageFromPacket(message, packet, this.settings);

                await this.iotHubClient.SendAsync(message);

                PerformanceCounters.MessagesSentPerSecond.Increment();
            }

            if (!this.IsInState(StateFlags.Closed))
            {
                switch (packet.QualityOfService)
                {
                    case QualityOfService.AtMostOnce:
                        // no response necessary
                        PerformanceCounters.InboundMessageProcessingTime.Register(startedTimestamp);
                        break;
                    case QualityOfService.AtLeastOnce:
                        Util.WriteMessageAsync(context, PubAckPacket.InResponseTo(packet))
                            .OnFault(ShutdownOnWriteFaultAction, context);
                        PerformanceCounters.InboundMessageProcessingTime.Register(startedTimestamp); // todo: assumes PUBACK is written out sync
                        break;
                    case QualityOfService.ExactlyOnce:
                        ShutdownOnError(context, "QoS 2 is not supported.");
                        break;
                    default:
                        throw new InvalidOperationException("Unexpected QoS level: " + packet.QualityOfService);
                }
            }
        }
        void ApplyRoutingConfiguration(Message message, PublishPacket packet)
        {
            RouteDestinationType routeType;
            if (this.topicNameRouter.TryMapTopicNameToRoute(packet.TopicName, out routeType, message.Properties))
            {
                // successfully matched topic against configured routes -> validate topic name
                string messageDeviceId;
                if (message.Properties.TryGetValue(DeviceIdParam, out messageDeviceId))
                {
                    if (!this.deviceId.Equals(messageDeviceId, StringComparison.Ordinal))
                    {
                        throw new InvalidOperationException(string.Format("Device ID provided in topic name ({0}) does not match ID of the device publishing message ({1}).",
                            messageDeviceId,
                            this.deviceId));
                    }
                    message.Properties.Remove(DeviceIdParam);
                }
            }
            else
            {
                if (MqttIotHubAdapterEventSource.Log.IsWarningEnabled)
                {
                    MqttIotHubAdapterEventSource.Log.Warning("Topic name could not be matched against any of the configured routes. Falling back to default telemetry settings.", packet.ToString());
                }
                routeType = RouteDestinationType.Telemetry;
                message.Properties[UnmatchedFlagPropertyName] = bool.TrueString;
                message.Properties[SubjectPropertyName] = packet.TopicName;
            }

            // once we have different routes, this will change to tackle different aspects of route types
            switch (routeType)
            {
                case RouteDestinationType.Telemetry:
                    break;
                default:
                    throw new ArgumentOutOfRangeException(string.Format("Unexpected route type: {0}", routeType));
            }
        }
        void ApplyRoutingConfiguration(IMessage message, PublishPacket packet)
        {
            RouteDestinationType routeType;
            if (this.messageRouter.TryRouteIncomingMessage(packet.TopicName, message, out routeType))
            {
                // successfully matched topic against configured routes -> validate topic name
                string messageDeviceId;
                if (message.Properties.TryGetValue(TemplateParameters.DeviceIdTemplateParam, out messageDeviceId))
                {
                    if (!this.DeviceId.Equals(messageDeviceId, StringComparison.Ordinal))
                    {
                        throw new InvalidOperationException(
                            $"Device ID provided in topic name ({messageDeviceId}) does not match ID of the device publishing message ({this.DeviceId})");
                    }
                    message.Properties.Remove(TemplateParameters.DeviceIdTemplateParam);
                }
            }
            else
            {
                if (!this.settings.PassThroughUnmatchedMessages)
                {
                    throw new InvalidOperationException($"Topic name `{packet.TopicName}` could not be matched against any of the configured routes.");
                }

                if (MqttIotHubAdapterEventSource.Log.IsWarningEnabled)
                {
                    MqttIotHubAdapterEventSource.Log.Warning("Topic name could not be matched against any of the configured routes. Falling back to default telemetry settings.", packet.ToString());
                }
                routeType = RouteDestinationType.Telemetry;
                message.Properties[this.settings.ServicePropertyPrefix + MessagePropertyNames.Unmatched] = bool.TrueString;
                message.Properties[this.settings.ServicePropertyPrefix + MessagePropertyNames.Subject] = packet.TopicName;
            }

            // once we have different routes, this will change to tackle different aspects of route types
            switch (routeType)
            {
                case RouteDestinationType.Telemetry:
                    break;
                default:
                    throw new ArgumentOutOfRangeException($"Unexpected route type: {routeType}");
            }
        }
 Task PublishToClientQos1Async(IChannelHandlerContext context, Message message, PublishPacket packet)
 {
     return this.publishPubAckProcessor.SendRequestAsync(context, packet, new AckPendingMessageState(message, packet));
 }
        async Task PublishToClientQos2Async(IChannelHandlerContext context, IMessage message, PublishPacket packet)
        {
            int packetId = packet.PacketId;
            IQos2MessageDeliveryState messageInfo = await this.qos2StateProvider.GetMessageAsync(this.identity, packetId);

            if (messageInfo != null && message.SequenceNumber != messageInfo.SequenceNumber)
            {
                await this.qos2StateProvider.DeleteMessageAsync(this.identity, packetId, messageInfo);
                messageInfo = null;
            }

            if (messageInfo == null)
            {
                await this.publishPubRecProcessor.SendRequestAsync(context, packet, new AckPendingMessageState(message, packet));
            }
            else
            {
                await this.PublishReleaseToClientAsync(context, packetId, message.LockToken, messageInfo, PreciseTimeSpan.FromStart);
            }
        }
        /// <summary>
        ///     Performs complete initialization of <see cref="MqttIotHubAdapter" /> based on received CONNECT packet.
        /// </summary>
        /// <param name="context"><see cref="IChannelHandlerContext" /> instance.</param>
        /// <param name="packet">CONNECT packet.</param>
        async void Connect(IChannelHandlerContext context, ConnectPacket packet)
        {
            bool connAckSent = false;

            Exception exception = null;
            try
            {
                if (!this.IsInState(StateFlags.WaitingForConnect))
                {
                    ShutdownOnError(context, "CONNECT has been received in current session already. Only one CONNECT is expected per session.");
                    return;
                }

                this.stateFlags = StateFlags.ProcessingConnect;
                AuthenticationResult authResult = await this.authProvider.AuthenticateAsync(packet.ClientId,
                    packet.Username, packet.Password, context.Channel.RemoteAddress);
                if (!authResult.IsSuccessful)
                {
                    connAckSent = true;
                    await Util.WriteMessageAsync(context, new ConnAckPacket
                    {
                        ReturnCode = ConnectReturnCode.RefusedNotAuthorized
                    });
                    PerformanceCounters.ConnectionFailedAuthPerSecond.Increment();
                    ShutdownOnError(context, "Authentication failed.");
                    return;
                }

                this.deviceId = authResult.DeviceId;

                this.iotHubClient = await this.deviceClientFactory(authResult);

                bool sessionPresent = await this.EstablishSessionStateAsync(this.deviceId, packet.CleanSession);

                this.keepAliveTimeout = this.DeriveKeepAliveTimeout(packet);

                if (packet.HasWill)
                {
                    var will = new PublishPacket(packet.WillQualityOfService, false, packet.WillRetain);
                    will.TopicName = packet.WillTopicName;
                    will.Payload = packet.WillMessage;
                    this.willPacket = will;
                }

                this.sessionContext = new Dictionary<string, string>
                {
                    { DeviceIdParam, this.deviceId }
                };

                this.StartReceiving(context);

                connAckSent = true;
                await Util.WriteMessageAsync(context, new ConnAckPacket
                {
                    SessionPresent = sessionPresent,
                    ReturnCode = ConnectReturnCode.Accepted
                });

                this.CompleteConnect(context);
            }
            catch (Exception ex)
            {
                exception = ex;
            }

            if (exception != null)
            {
                if (!connAckSent)
                {
                    try
                    {
                        await Util.WriteMessageAsync(context, new ConnAckPacket
                        {
                            ReturnCode = ConnectReturnCode.RefusedServerUnavailable
                        });
                    }
                    catch (Exception ex)
                    {
                        if (MqttIotHubAdapterEventSource.Log.IsVerboseEnabled)
                        {
                            MqttIotHubAdapterEventSource.Log.Verbose("Error sending 'Server Unavailable' CONNACK.", ex.ToString());
                        }
                    }
                }

                ShutdownOnError(context, "CONNECT", exception);
            }
        }
        public static async Task<PublishPacket> ComposePublishPacketAsync(IChannelHandlerContext context, IMessage message,
            QualityOfService qos, string topicName, IByteBufferAllocator allocator)
        {
            bool duplicate = message.DeliveryCount > 0;

            var packet = new PublishPacket(qos, duplicate, false);
            packet.TopicName = topicName;
            if (qos > QualityOfService.AtMostOnce)
            {
                int packetId = unchecked((int)message.SequenceNumber) & 0x3FFF; // clear bits #14 and #15
                switch (qos)
                {
                    case QualityOfService.AtLeastOnce:
                        break;
                    case QualityOfService.ExactlyOnce:
                        packetId |= 0x8000; // set bit #15
                        break;
                    default:
                        throw new ArgumentOutOfRangeException(nameof(qos), qos, null);
                }
                packet.PacketId = packetId + 1;
            }
            using (Stream payloadStream = message.Payload)
            {
                long streamLength = payloadStream.Length;
                if (streamLength > int.MaxValue)
                {
                    throw new InvalidOperationException($"Message size ({streamLength} bytes) is too big to process.");
                }

                int length = (int)streamLength;
                IByteBuffer buffer = allocator.Buffer(length, length);
                await buffer.WriteBytesAsync(payloadStream, length);
                Contract.Assert(buffer.ReadableBytes == length);

                packet.Payload = buffer;
            }
            return packet;
        }
        public static Message CompleteMessageFromPacket(Message message, PublishPacket packet, Settings settings)
        {
            message.MessageId = Guid.NewGuid().ToString("N");
            if (packet.RetainRequested)
            {
                message.Properties[settings.RetainPropertyName] = IotHubTrueString;
            }
            if (packet.Duplicate)
            {
                message.Properties[settings.DupPropertyName] = IotHubTrueString;
            }

            return message;
        }
Exemple #24
0
        public static async Task<PublishPacket> ComposePublishPacketAsync(IChannelHandlerContext context, Message message, QualityOfService qos, string topicName)
        {
            var packet = new PublishPacket(qos, false, false);
            packet.TopicName = PopulateMessagePropertiesFromMessage(topicName, message);
            if (qos > QualityOfService.AtMostOnce)
            {
                int packetId = GetNextPacketId();
                switch (qos)
                {
                    case QualityOfService.AtLeastOnce:
                        packetId &= 0x7FFF; // clear 15th bit
                        break;
                    case QualityOfService.ExactlyOnce:
                        packetId |= 0x8000; // set 15th bit
                        break;
                    default:
                        throw new ArgumentOutOfRangeException(nameof(qos), qos, null);
                }
                packet.PacketId = packetId;
            }
            using (Stream payloadStream = message.GetBodyStream())
            {
                long streamLength = payloadStream.Length;
                if (streamLength > MaxPayloadSize)
                {
                    throw new InvalidOperationException($"Message size ({streamLength} bytes) is too big to process. Maximum allowed payload size is {MaxPayloadSize}");
                }

                int length = (int)streamLength;
                IByteBuffer buffer = context.Channel.Allocator.Buffer(length, length);
                await buffer.WriteBytesAsync(payloadStream, length);
                Contract.Assert(buffer.ReadableBytes == length);

                packet.Payload = buffer;
            }
            return packet;
        }
        Task AcceptMessageAsync(IChannelHandlerContext context, PublishPacket publish)
        {
            Message message;
            try
            {
                var buffer = new byte[publish.Payload.ReadableBytes];
                publish.Payload.GetBytes(0, buffer);
                var bodyStream = new MemoryStream(buffer);
                bodyStream.Position = 0;

                message = new Message(bodyStream);

                Util.PopulateMessagePropertiesFromPacket(message, publish);
            }
            catch (Exception ex)
            {
                ShutdownOnError(context, ex);
                return TaskConstants.Completed;
            }
            finally
            {
                publish.Release();
            }
            this.onMessageReceived(message);
            return TaskConstants.Completed;
        }
Exemple #26
0
 public static void PopulateMessagePropertiesFromPacket(Message message, PublishPacket publish)
 {
     message.LockToken = publish.QualityOfService == QualityOfService.AtLeastOnce ? publish.PacketId.ToString() : null;
     Dictionary<string, string> properties = UrlEncodedDictionarySerializer.Deserialize(publish.TopicName, publish.TopicName.NthIndexOf('/', 0, 4) + 1);
     foreach (KeyValuePair<string, string> property in properties)
     {
         string propertyName;
         if (ToSystemPropertiesMap.TryGetValue(property.Key, out propertyName))
         {
             message.SystemProperties[propertyName] = ConvertToSystemProperty(property);
         }
         else
         {
             message.Properties[property.Key] = property.Value;
         }
     }
 }
 Task SendAckAsync(IChannelHandlerContext context, PublishPacket publish)
 {
     this.ResumeReadingIfNecessary(context);
     return Util.WriteMessageAsync(context, PubAckPacket.InResponseTo(publish), ShutdownOnWriteErrorHandler);
 }
 public AckPendingMessageState(IMessage message, PublishPacket packet)
     : this(message.SequenceNumber, packet.PacketId, packet.QualityOfService, message.LockToken)
 {
 }