public async Task SendAckAsync(string clientId, IFlowPacket ack, IMqttChannel <IPacket> channel, PendingMessageStatus status = PendingMessageStatus.PendingToSend) { if ((ack.Type == MqttPacketType.PublishReceived || ack.Type == MqttPacketType.PublishRelease) && status == PendingMessageStatus.PendingToSend) { SavePendingAcknowledgement(ack, clientId); } if (!channel.IsConnected) { return; } await channel.SendAsync(ack) .ConfigureAwait(continueOnCapturedContext: false); if (ack.Type == MqttPacketType.PublishReceived) { await MonitorAckAsync <PublishRelease> (ack, clientId, channel) .ConfigureAwait(continueOnCapturedContext: false); } else if (ack.Type == MqttPacketType.PublishRelease) { await MonitorAckAsync <PublishComplete> (ack, clientId, channel) .ConfigureAwait(continueOnCapturedContext: false); } }
public async Task ExecuteAsync(string clientId, IPacket input, IMqttChannel <IPacket> channel) { if (input.Type != MqttPacketType.Disconnect) { return; } await Task.Run(() => { Disconnect disconnect = input as Disconnect; _tracer.Info(ServerProperties.DisconnectFlow_Disconnecting(clientId)); _willRepository.Delete(clientId); ClientSession session = _sessionRepository.Read(clientId); if (session == null) { throw new MqttException(ServerProperties.SessionRepository_ClientSessionNotFound(clientId)); } if (session.Clean) { _sessionRepository.Delete(session.Id); _tracer.Info(ServerProperties.Server_DeletedSessionOnDisconnect(clientId)); } _connectionProvider.RemoveConnection(clientId); }); }
public IMqttChannel <IPacket> Create(IMqttChannel <byte[]> binaryChannel) { IEnumerable <IFormatter> formatters = GetFormatters(); PacketManager packetManager = new PacketManager(formatters); return(new PacketChannel(binaryChannel, packetManager, _configuration)); }
void ProcessChannel(IMqttChannel <byte[]> binaryChannel) { _tracer.Verbose(ServerProperties.Server_NewSocketAccepted); IMqttChannel <IPacket> packetChannel = _channelFactory.Create(binaryChannel); ServerPacketListener packetListener = new ServerPacketListener(packetChannel, _connectionProvider, _flowProvider, _configuration); packetListener.Listen(); packetListener .PacketStream .Subscribe(_ => { }, ex => { _tracer.Error(ex, ServerProperties.Server_PacketsObservableError); packetChannel.Dispose(); packetListener.Dispose(); }, () => { _tracer.Warn(ServerProperties.Server_PacketsObservableCompleted); packetChannel.Dispose(); packetListener.Dispose(); } ); _channels.Add(packetChannel); }
public async Task ExecuteAsync(string clientId, IPacket input, IMqttChannel <IPacket> channel) { if (input.Type != MqttPacketType.Unsubscribe) { return; } Unsubscribe unsubscribe = input as Unsubscribe; ClientSession session = _sessionRepository.Read(clientId); if (session == null) { throw new MqttException(ServerProperties.SessionRepository_ClientSessionNotFound(clientId)); } foreach (string topic in unsubscribe.Topics) { ClientSubscription subscription = session.GetSubscriptions().FirstOrDefault(s => s.TopicFilter == topic); if (subscription != null) { session.RemoveSubscription(subscription); } } _sessionRepository.Update(session); await channel.SendAsync(new UnsubscribeAck( unsubscribe.PacketId )); }
public async Task ExecuteAsync(string clientId, IPacket input, IMqttChannel <IPacket> channel) { if (input.Type != MqttPacketType.ConnectAck) { return; } var ack = input as ConnectAck; if (ack.Status != MqttConnectionStatus.Accepted) { return; } var session = sessionRepository.Read(clientId); if (session == null) { throw new MqttException(string.Format(Properties.Resources.SessionRepository_ClientSessionNotFound, clientId)); } await SendPendingMessagesAsync(session, channel) .ConfigureAwait(continueOnCapturedContext: false); await SendPendingAcknowledgementsAsync(session, channel) .ConfigureAwait(continueOnCapturedContext: false); }
private static int ReadBodyLength(IMqttChannel channel, byte initialEncodedByte, byte[] singleByteBuffer, CancellationToken cancellationToken) { // Alorithm taken from https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html. var multiplier = 128; var value = initialEncodedByte & 127; int encodedByte = initialEncodedByte; while ((encodedByte & 128) != 0) { cancellationToken.ThrowIfCancellationRequested(); // Here the async/await pattern is not used becuase the overhead of context switches // is too big for reading 1 byte in a row. We expect that the remaining data was sent // directly after the initial bytes. If the client disconnects just in this moment we // will get an exception anyway. encodedByte = ReadByte(channel, singleByteBuffer, cancellationToken); value += (byte)(encodedByte & 127) * multiplier; if (multiplier > 128 * 128 * 128) { throw new MqttProtocolViolationException("Remaining length is invalid."); } multiplier *= 128; } return(value); }
public PacketChannel(IMqttChannel <byte[]> innerChannel, IPacketManager manager, MqttConfiguration configuration) { _innerChannel = innerChannel; _manager = manager; _receiver = new ReplaySubject <IPacket>(window: TimeSpan.FromSeconds(configuration.WaitTimeoutSecs)); _sender = new ReplaySubject <IPacket>(window: TimeSpan.FromSeconds(configuration.WaitTimeoutSecs)); _subscription = innerChannel .ReceiverStream .Subscribe(async bytes => { try { IPacket packet = await _manager.GetPacketAsync(bytes); _receiver.OnNext(packet); } catch (MqttException ex) { _receiver.OnError(ex); } }, onError: ex => _receiver.OnError(ex), onCompleted: () => _receiver.OnCompleted()); }
public async Task ExecuteAsync(string clientId, IPacket input, IMqttChannel <IPacket> channel) { if (input.Type != MqttPacketType.Disconnect) { return; } await Task.Run(() => { var disconnect = input as Disconnect; tracer.Info(Server.Properties.Resources.DisconnectFlow_Disconnecting, clientId); willRepository.Delete(clientId); var session = sessionRepository.Read(clientId); if (session == null) { throw new MqttException(string.Format(Properties.Resources.SessionRepository_ClientSessionNotFound, clientId)); } if (session.Clean) { sessionRepository.Delete(session.Id); tracer.Info(Server.Properties.Resources.Server_DeletedSessionOnDisconnect, clientId); } connectionProvider.RemoveConnection(clientId); }); }
public static async Task <MqttFixedHeader> ReadFixedHeaderAsync(IMqttChannel channel, byte[] fixedHeaderBuffer, byte[] singleByteBuffer, CancellationToken cancellationToken) { // The MQTT fixed header contains 1 byte of flags and at least 1 byte for the remaining data length. // So in all cases at least 2 bytes must be read for a complete MQTT packet. // async/await is used here because the next packet is received in a couple of minutes so the performance // impact is acceptable according to a useless waiting thread. var buffer = fixedHeaderBuffer; var totalBytesRead = 0; while (totalBytesRead < buffer.Length) { var bytesRead = await channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false); if (bytesRead <= 0) { cancellationToken.ThrowIfCancellationRequested(); ExceptionHelper.ThrowGracefulSocketClose(); } totalBytesRead += bytesRead; } var hasRemainingLength = buffer[1] != 0; if (!hasRemainingLength) { return(new MqttFixedHeader(buffer[0], 0)); } var bodyLength = ReadBodyLength(channel, buffer[1], singleByteBuffer, cancellationToken); return(new MqttFixedHeader(buffer[0], bodyLength)); }
public IMqttChannel <IPacket> Create(IMqttChannel <byte[]> binaryChannel) { var formatters = GetFormatters(); var packetManager = new PacketManager(formatters); return(new PacketChannel(binaryChannel, packetManager, configuration)); }
public async Task SendPublishAsync(string clientId, Publish message, IMqttChannel <IPacket> channel, PendingMessageStatus status = PendingMessageStatus.PendingToSend) { if (channel == null || !channel.IsConnected) { SaveMessage(message, clientId, PendingMessageStatus.PendingToSend); return; } var qos = configuration.GetSupportedQos(message.QualityOfService); if (qos != MqttQualityOfService.AtMostOnce && status == PendingMessageStatus.PendingToSend) { SaveMessage(message, clientId, PendingMessageStatus.PendingToAcknowledge); } await channel.SendAsync(message) .ConfigureAwait(continueOnCapturedContext: false); if (qos == MqttQualityOfService.AtLeastOnce) { await MonitorAckAsync <PublishAck> (message, clientId, channel) .ConfigureAwait(continueOnCapturedContext: false); } else if (qos == MqttQualityOfService.ExactlyOnce) { await MonitorAckAsync <PublishReceived> (message, clientId, channel).ConfigureAwait(continueOnCapturedContext: false); await channel .ReceiverStream .ObserveOn(NewThreadScheduler.Default) .OfType <PublishComplete> () .FirstOrDefaultAsync(x => x.PacketId == message.PacketId.Value); } }
public void Setup() { var factory = new MqttFactory(); var tcpServer = new MqttTcpServerAdapter(new MqttNetEventLogger()); tcpServer.ClientHandler += args => { _serverChannel = (IMqttChannel)args.GetType().GetField("_channel", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) .GetValue(args); return(Task.CompletedTask); }; _mqttServer = factory.CreateMqttServer(new[] { tcpServer }, new MqttNetEventLogger()); var serverOptions = new MqttServerOptionsBuilder().Build(); _mqttServer.StartAsync(serverOptions).GetAwaiter().GetResult(); var clientOptions = new MqttClientOptionsBuilder() .WithTcpServer("localhost").Build(); var tcpOptions = (MqttClientTcpOptions)clientOptions.ChannelOptions; _clientChannel = new MqttTcpChannel(new MqttClientOptions { ChannelOptions = tcpOptions }); _clientChannel.ConnectAsync(CancellationToken.None).GetAwaiter().GetResult(); }
async Task SendPendingMessagesAsync(ClientSession session, IMqttChannel <IPacket> channel) { foreach (var pendingMessage in session.GetPendingMessages()) { var publish = new Publish(pendingMessage.Topic, pendingMessage.QualityOfService, pendingMessage.Retain, pendingMessage.Duplicated, pendingMessage.PacketId) { Payload = pendingMessage.Payload }; if (pendingMessage.Status == PendingMessageStatus.PendingToSend) { session.RemovePendingMessage(pendingMessage); sessionRepository.Update(session); await senderFlow.SendPublishAsync(session.Id, publish, channel) .ConfigureAwait(continueOnCapturedContext: false); } else { await senderFlow.SendPublishAsync(session.Id, publish, channel, PendingMessageStatus.PendingToAcknowledge) .ConfigureAwait(continueOnCapturedContext: false); } } }
public async Task ExecuteAsync(string clientId, IPacket input, IMqttChannel <IPacket> channel) { if (input.Type != MqttPacketType.Unsubscribe) { return; } var unsubscribe = input as Unsubscribe; var session = sessionRepository.Read(clientId); if (session == null) { throw new MqttException(string.Format(Properties.Resources.SessionRepository_ClientSessionNotFound, clientId)); } foreach (var topic in unsubscribe.Topics) { var subscription = session.GetSubscriptions().FirstOrDefault(s => s.TopicFilter == topic); if (subscription != null) { session.RemoveSubscription(subscription); } } sessionRepository.Update(session); await channel.SendAsync(new UnsubscribeAck (unsubscribe.PacketId)) .ConfigureAwait(continueOnCapturedContext: false); }
async Task HandlePublishAsync(string clientId, Publish publish, IMqttChannel <IPacket> channel) { Validate(publish, clientId); var qos = configuration.GetSupportedQos(publish.QualityOfService); var session = sessionRepository.Read(clientId); if (session == null) { throw new MqttException(string.Format(Properties.Resources.SessionRepository_ClientSessionNotFound, clientId)); } if (qos == MqttQualityOfService.ExactlyOnce && session.GetPendingAcknowledgements().Any(ack => ack.Type == MqttPacketType.PublishReceived && ack.PacketId == publish.PacketId.Value)) { await SendQosAck(clientId, qos, publish, channel) .ConfigureAwait(continueOnCapturedContext: false); return; } await SendQosAck(clientId, qos, publish, channel) .ConfigureAwait(continueOnCapturedContext: false); await ProcessPublishAsync(publish, clientId) .ConfigureAwait(continueOnCapturedContext: false); }
private static async Task <int> ReadBodyLengthAsync(IMqttChannel channel, byte initialEncodedByte, byte[] singleByteBuffer, CancellationToken cancellationToken) { var offset = 0; var multiplier = 128; var value = initialEncodedByte & 127; int encodedByte = initialEncodedByte; while ((encodedByte & 128) != 0) { offset++; if (offset > 3) { throw new MqttProtocolViolationException("Remaining length is invalid."); } cancellationToken.ThrowIfCancellationRequested(); encodedByte = await ReadByteAsync(channel, singleByteBuffer, cancellationToken).ConfigureAwait(false); value += (byte)(encodedByte & 127) * multiplier; multiplier *= 128; } return(value); }
internal MqttConnectedClient(IMqttChannel <IPacket> packetChannel, IProtocolFlowProvider flowProvider, IRepositoryProvider repositoryProvider, IPacketIdProvider packetIdProvider, MqttConfiguration configuration) : base(packetChannel, flowProvider, repositoryProvider, packetIdProvider, configuration) { }
public async Task ExecuteAsync(string clientId, IPacket input, IMqttChannel <IPacket> channel) { if (input.Type != MqttPacketType.Connect) { return; } var connect = input as Connect; if (!authenticationProvider.Authenticate(clientId, connect.UserName, connect.Password)) { throw new MqttConnectionException(MqttConnectionStatus.BadUserNameOrPassword); } var session = sessionRepository.Read(clientId); var sessionPresent = connect.CleanSession ? false : session != null; if (connect.CleanSession && session != null) { sessionRepository.Delete(session.Id); session = null; tracer.Info(Server.Properties.Resources.Server_CleanedOldSession, clientId); } var sendPendingMessages = false; if (session == null) { session = new ClientSession(clientId, connect.CleanSession); sessionRepository.Create(session); tracer.Info(Server.Properties.Resources.Server_CreatedSession, clientId); } else { sendPendingMessages = true; } if (connect.Will != null) { var connectionWill = new ConnectionWill(clientId, connect.Will); willRepository.Create(connectionWill); } await channel.SendAsync(new ConnectAck (MqttConnectionStatus.Accepted, sessionPresent)) .ConfigureAwait(continueOnCapturedContext: false); if (sendPendingMessages) { await SendPendingMessagesAsync(session, channel) .ConfigureAwait(continueOnCapturedContext: false); await SendPendingAcknowledgementsAsync(session, channel) .ConfigureAwait(continueOnCapturedContext: false); } }
public async Task ExecuteAsync(string clientId, IPacket input, IMqttChannel <IPacket> channel) { if (input.Type != MqttPacketType.Connect) { return; } Connect connect = input as Connect; if (!_authenticationProvider.Authenticate(clientId, connect.UserName, connect.Password)) { throw new MqttConnectionException(MqttConnectionStatus.BadUserNameOrPassword); } ClientSession session = _sessionRepository.Read(clientId); bool sessionPresent = connect.CleanSession ? false : session != null; _tracer.Info($"Client connecting with protocol level {connect.ProtocolLelvel}."); if (connect.CleanSession && session != null) { _sessionRepository.Delete(session.Id); session = null; _tracer.Info(ServerProperties.Server_CleanedOldSession(clientId)); } bool sendPendingMessages = false; if (session == null) { session = new ClientSession(clientId, connect.CleanSession); _sessionRepository.Create(session); _tracer.Info(ServerProperties.Server_CreatedSession(clientId)); } else { sendPendingMessages = true; } if (connect.Will != null) { ConnectionWill connectionWill = new ConnectionWill(clientId, connect.Will); _willRepository.Create(connectionWill); } await channel.SendAsync(new ConnectAck( MqttConnectionStatus.Accepted, sessionPresent )); if (sendPendingMessages) { await SendPendingMessagesAsync(session, channel); await SendPendingAcknowledgementsAsync(session, channel); } }
public async Task ExecuteAsync(string clientId, IPacket input, IMqttChannel <IPacket> channel) { if (input.Type != MqttPacketType.PingRequest) { return; } await channel.SendAsync(new PingResponse()); }
async Task InitializeChannelAsync() { Channel = await channelFactory .CreateAsync() .ConfigureAwait(continueOnCapturedContext: false); packetListener = new ClientPacketListener(Channel, flowProvider, configuration); packetListener.Listen(); ObservePackets(); }
public async Task ExecuteAsync(string clientId, IPacket input, IMqttChannel <IPacket> channel) { if (input.Type != MqttPacketType.PingRequest) { return; } await channel.SendAsync(new PingResponse()) .ConfigureAwait(continueOnCapturedContext: false); }
public ClientPacketListener(IMqttChannel <IPacket> channel, IProtocolFlowProvider flowProvider, MqttConfiguration configuration) { this.channel = channel; this.flowProvider = flowProvider; this.configuration = configuration; packets = new ReplaySubject <IPacket> (window: TimeSpan.FromSeconds(configuration.WaitTimeoutSecs)); flowRunner = TaskRunner.Get(); }
public void when_getting_connection_from_client_then_succeeds() { ConnectionProvider provider = new ConnectionProvider(); string clientId = Guid.NewGuid().ToString(); provider.AddConnection(clientId, Mock.Of <IMqttChannel <IPacket> >(c => c.IsConnected == true)); IMqttChannel <IPacket> connection = provider.GetConnection(clientId); Assert.NotNull(connection); }
public void when_getting_connection_from_disconnected_client_then_no_connection_is_returned() { ConnectionProvider provider = new ConnectionProvider(); string clientId = Guid.NewGuid().ToString(); provider.AddConnection(clientId, Mock.Of <IMqttChannel <IPacket> >(c => c.IsConnected == false)); IMqttChannel <IPacket> connection = provider.GetConnection(clientId); Assert.Null(connection); }
public void AddConnection(string clientId, IMqttChannel <IPacket> connection) { if (_connections.TryGetValue(clientId, out _)) { _tracer.Warn(ServerProperties.ConnectionProvider_ClientIdExists(clientId)); RemoveConnection(clientId); } _connections.TryAdd(clientId, connection); }
async Task SendPendingMessagesAsync(ClientSession session, IMqttChannel <IPacket> channel) { foreach (PendingMessage pendingMessage in session.GetPendingMessages()) { Publish publish = new Publish(pendingMessage.Topic, pendingMessage.QualityOfService, pendingMessage.Retain, pendingMessage.Duplicated, pendingMessage.PacketId); await _senderFlow .SendPublishAsync(session.Id, publish, channel, PendingMessageStatus.PendingToAcknowledge); } }
public MqttChannelAdapter(IMqttChannel channel, IMqttPacketSerializer serializer, IMqttNetChildLogger logger) { if (logger == null) { throw new ArgumentNullException(nameof(logger)); } _channel = channel ?? throw new ArgumentNullException(nameof(channel)); PacketSerializer = serializer ?? throw new ArgumentNullException(nameof(serializer)); _logger = logger.CreateChildLogger(nameof(MqttChannelAdapter)); }
public void when_keep_alive_enabled_and_no_packet_received_then_times_out() { Mock <IConnectionProvider> connectionProvider = new Mock <IConnectionProvider>(); Mock <IServerPublishReceiverFlow> serverPublishReceiverFlow = new Mock <IServerPublishReceiverFlow>(); Mock <IProtocolFlowProvider> flowProvider = new Mock <IProtocolFlowProvider>(); serverPublishReceiverFlow.Setup(f => f.SendWillAsync(It.IsAny <string>())) .Returns(Task.FromResult(true)); flowProvider.Setup(p => p.GetFlow <IServerPublishReceiverFlow>()) .Returns(serverPublishReceiverFlow.Object); flowProvider.Setup(p => p.GetFlow(It.IsAny <MqttPacketType>())) .Returns(Mock.Of <IProtocolFlow>()); MqttConfiguration configuration = new MqttConfiguration { WaitTimeoutSecs = 10 }; Subject <IPacket> receiver = new Subject <IPacket>(); Subject <IPacket> sender = new Subject <IPacket>(); Mock <IMqttChannel <IPacket> > packetChannelMock = new Mock <IMqttChannel <IPacket> >(); packetChannelMock.Setup(c => c.ReceiverStream).Returns(receiver); packetChannelMock.Setup(c => c.SenderStream).Returns(sender); IMqttChannel <IPacket> packetChannel = packetChannelMock.Object; ServerPacketListener listener = new ServerPacketListener(packetChannel, connectionProvider.Object, flowProvider.Object, configuration); listener.Listen(); ManualResetEventSlim timeoutSignal = new ManualResetEventSlim(initialState: false); listener.PacketStream.Subscribe(_ => { }, ex => { timeoutSignal.Set(); }); string clientId = Guid.NewGuid().ToString(); ushort keepAlive = 1; Connect connect = new Connect(clientId, cleanSession: true, MqttProtocol.SupportedLevel) { KeepAlive = keepAlive }; receiver.OnNext(connect); ConnectAck connectAck = new ConnectAck(MqttConnectionStatus.Accepted, existingSession: false); sender.OnNext(connectAck); bool timeoutOccurred = timeoutSignal.Wait(((int)((keepAlive + 1) * 1.5) * 1000)); Assert.True(timeoutOccurred); }