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; }
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); }
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; }
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; }
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) { }