From 2047f445548a1c52e0d3942716da8af464812d39 Mon Sep 17 00:00:00 2001 From: SeppPenner Date: Sun, 3 Jan 2021 21:46:53 +0100 Subject: [PATCH 01/29] Added client documentation. --- .../IManagedMqttClient.cs | 71 ++++++- .../Connecting/IMqttClientConnectedHandler.cs | 5 + .../MqttClientAuthenticateResult.cs | 68 +++++++ .../MqttClientConnectedEventArgs.cs | 4 + .../IMqttClientDisconnectedHandler.cs | 5 + .../MqttClientDisconnectOptions.cs | 8 + .../MqttClientDisconnectedEventArgs.cs | 14 ++ ...ttExtendedAuthenticationExchangeHandler.cs | 5 + ...ttExtendedAuthenticationExchangeContext.cs | 26 +++ .../MqttExtendedAuthenticationExchangeData.cs | 21 ++ Source/MQTTnet/Client/IMqttClient.cs | 56 ++++++ Source/MQTTnet/Client/IMqttClientFactory.cs | 48 ++++- .../Client/Options/IMqttClientOptions.cs | 96 +++++++++ ...entCertificateValidationCallbackContext.cs | 12 ++ .../Client/Options/MqttClientOptions.cs | 94 +++++++++ .../Options/MqttClientOptionsBuilder.cs | 167 ++++++++++++++++ .../Publishing/MqttClientPublishResult.cs | 18 ++ .../IMqttApplicationMessageReceivedHandler.cs | 5 + .../Subscribing/MqttClientSubscribeOptions.cs | 18 ++ .../MqttClientSubscribeOptionsBuilder.cs | 41 +++- .../MqttClientSubscribeResultItem.cs | 8 + .../MqttClientUnsubscribeOptions.cs | 11 ++ .../MqttClientUnsubscribeOptionsBuilder.cs | 27 +++ .../MqttClientUnsubscribeResultItem.cs | 8 + .../MQTTnet/IApplicationMessagePublisher.cs | 6 + Source/MQTTnet/IApplicationMessageReceiver.cs | 4 + Source/MQTTnet/IMqttFactory.cs | 6 + Source/MQTTnet/MqttApplicationMessage.cs | 71 +++++++ .../MQTTnet/MqttApplicationMessageBuilder.cs | 183 +++++++++++++++++- .../MqttApplicationMessageExtensions.cs | 5 + Source/MQTTnet/MqttTopicFilter.cs | 22 ++- Source/MQTTnet/MqttTopicFilterBuilder.cs | 40 ++++ Source/MQTTnet/Server/IMqttServerFactory.cs | 23 +++ 33 files changed, 1176 insertions(+), 20 deletions(-) diff --git a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs b/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs index cc2490cb..4e06eaf9 100644 --- a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs +++ b/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs @@ -16,36 +16,95 @@ namespace MQTTnet.Extensions.ManagedClient /// IMqttClient InternalClient { get; } + /// + /// Gets a value indicating whether the client is started or not. + /// bool IsStarted { get; } - + + /// + /// Gets a value indicating whether the client is connected or not. + /// bool IsConnected { get; } + /// + /// Gets a pending application message count. + /// int PendingApplicationMessagesCount { get; } + /// + /// Gets the MQTT client options. + /// IManagedMqttClientOptions Options { get; } + /// + /// Gets or sets the connected handler. + /// IMqttClientConnectedHandler ConnectedHandler { get; set; } - + + /// + /// Gets or sets the disconnected handler. + /// IMqttClientDisconnectedHandler DisconnectedHandler { get; set; } + /// + /// Gets or sets the application message process handler. + /// IApplicationMessageProcessedHandler ApplicationMessageProcessedHandler { get; set; } - + + /// + /// Gets or sets the application message skipped handler. + /// IApplicationMessageSkippedHandler ApplicationMessageSkippedHandler { get; set; } + /// + /// Gets or sets the connecting failed handler. + /// IConnectingFailedHandler ConnectingFailedHandler { get; set; } + /// + /// Gets or sets the synchronizing subscriptions failed handler. + /// ISynchronizingSubscriptionsFailedHandler SynchronizingSubscriptionsFailedHandler { get; set; } + /// + /// Starts the client. + /// + /// The options. + /// A representing any asynchronous operation. Task StartAsync(IManagedMqttClientOptions options); - + + /// + /// Stops the client. + /// + /// A representing any asynchronous operation. Task StopAsync(); - + + /// + /// Pings the server. + /// + /// A token to cancel the task. + /// A representing any asynchronous operation. Task PingAsync(CancellationToken cancellationToken); + /// + /// Subscribes the client. + /// + /// The topic filters. + /// A representing any asynchronous operation. Task SubscribeAsync(IEnumerable topicFilters); - + + /// + /// Unsubscribes the client. + /// + /// The topics. + /// A representing any asynchronous operation. Task UnsubscribeAsync(IEnumerable topics); + /// + /// Publishes a message from the client. + /// + /// The application messages. + /// A representing any asynchronous operation. Task PublishAsync(ManagedMqttApplicationMessage applicationMessages); } } \ No newline at end of file diff --git a/Source/MQTTnet/Client/Connecting/IMqttClientConnectedHandler.cs b/Source/MQTTnet/Client/Connecting/IMqttClientConnectedHandler.cs index 8b7be27e..233ee577 100644 --- a/Source/MQTTnet/Client/Connecting/IMqttClientConnectedHandler.cs +++ b/Source/MQTTnet/Client/Connecting/IMqttClientConnectedHandler.cs @@ -4,6 +4,11 @@ namespace MQTTnet.Client.Connecting { public interface IMqttClientConnectedHandler { + /// + /// Handles the events when a client gets connected. + /// + /// The event args. + /// A representing any asynchronous operation. Task HandleConnectedAsync(MqttClientConnectedEventArgs eventArgs); } } diff --git a/Source/MQTTnet/Client/Connecting/MqttClientAuthenticateResult.cs b/Source/MQTTnet/Client/Connecting/MqttClientAuthenticateResult.cs index 7419fae0..dfcaaff1 100644 --- a/Source/MQTTnet/Client/Connecting/MqttClientAuthenticateResult.cs +++ b/Source/MQTTnet/Client/Connecting/MqttClientAuthenticateResult.cs @@ -7,42 +7,110 @@ namespace MQTTnet.Client.Connecting // TODO: Consider renaming this to _MqttClientConnectResult_ public class MqttClientAuthenticateResult { + /// + /// Gets or sets the result code. + /// Hint: MQTT 5 feature only. + /// public MqttClientConnectResultCode ResultCode { get; set; } + /// + /// Gets or sets a value indicating whether the session is present or not. + /// public bool IsSessionPresent { get; set; } + /// + /// Gets or sets a value indicating whether wildcard subscriptions are available. + /// public bool? WildcardSubscriptionAvailable { get; set; } + /// + /// Gets or sets a value indicating whether retained messages are available. + /// public bool? RetainAvailable { get; set; } + /// + /// Gets or sets the assigned client identifier. + /// public string AssignedClientIdentifier { get; set; } + /// + /// Gets or sets the authentication method. + /// Hint: MQTT 5 feature only. + /// public string AuthenticationMethod { get; set; } + /// + /// Gets or sets the authentication data. + /// Hint: MQTT 5 feature only. + /// public byte[] AuthenticationData { get; set; } + /// + /// Gets or sets the maximum packet size. + /// public uint? MaximumPacketSize { get; set; } + /// + /// Gets or sets the reason string. + /// Hint: MQTT 5 feature only. + /// public string ReasonString { get; set; } + /// + /// Gets or sets the receive maximum. + /// public ushort? ReceiveMaximum { get; set; } + /// + /// Gets or sets the maximum quality od service level. + /// public MqttQualityOfServiceLevel MaximumQoS { get; set; } + /// + /// Gets or sets the response information. + /// Hint: MQTT 5 feature only. + /// public string ResponseInformation { get; set; } + /// + /// Gets or sets the topic alias maximum. + /// public ushort? TopicAliasMaximum { get; set; } + /// + /// Gets or sets the server reference. + /// public string ServerReference { get; set; } + /// + /// Gets or sets the server keep alive. + /// public ushort? ServerKeepAlive { get; set; } + /// + /// Gets or sets the session expiry interval. + /// public uint? SessionExpiryInterval { get; set; } + /// + /// Gets or sets a value indicating whether the subscription identifiers are available or not. + /// Hint: MQTT 5 feature only. + /// public bool? SubscriptionIdentifiersAvailable { get; set; } + /// + /// Gets or sets a value indicating whether the shared subscriptions are available or not. + /// Hint: MQTT 5 feature only. + /// public bool? SharedSubscriptionAvailable { get; set; } + /// + /// Gets or sets the user properties. + /// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet. + /// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber. + /// The feature is very similar to the HTTP header concept. + /// Hint: MQTT 5 feature only. + /// public List UserProperties { get; set; } } } diff --git a/Source/MQTTnet/Client/Connecting/MqttClientConnectedEventArgs.cs b/Source/MQTTnet/Client/Connecting/MqttClientConnectedEventArgs.cs index e7f97b9e..02cd47f5 100644 --- a/Source/MQTTnet/Client/Connecting/MqttClientConnectedEventArgs.cs +++ b/Source/MQTTnet/Client/Connecting/MqttClientConnectedEventArgs.cs @@ -9,6 +9,10 @@ namespace MQTTnet.Client.Connecting AuthenticateResult = authenticateResult ?? throw new ArgumentNullException(nameof(authenticateResult)); } + /// + /// Gets the authentication result. + /// Hint: MQTT 5 feature only. + /// public MqttClientAuthenticateResult AuthenticateResult { get; } } } diff --git a/Source/MQTTnet/Client/Disconnecting/IMqttClientDisconnectedHandler.cs b/Source/MQTTnet/Client/Disconnecting/IMqttClientDisconnectedHandler.cs index 14347bf0..81e0b9ba 100644 --- a/Source/MQTTnet/Client/Disconnecting/IMqttClientDisconnectedHandler.cs +++ b/Source/MQTTnet/Client/Disconnecting/IMqttClientDisconnectedHandler.cs @@ -4,6 +4,11 @@ namespace MQTTnet.Client.Disconnecting { public interface IMqttClientDisconnectedHandler { + /// + /// Handles the events when a client gets disconnected. + /// + /// The event args. + /// A representing any asynchronous operation. Task HandleDisconnectedAsync(MqttClientDisconnectedEventArgs eventArgs); } } diff --git a/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectOptions.cs b/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectOptions.cs index 995806e7..97ab6443 100644 --- a/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectOptions.cs +++ b/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectOptions.cs @@ -2,8 +2,16 @@ { public class MqttClientDisconnectOptions { + /// + /// Gets or sets the reason code. + /// Hint: MQTT 5 feature only. + /// public MqttClientDisconnectReason ReasonCode { get; set; } = MqttClientDisconnectReason.NormalDisconnection; + /// + /// Gets or sets the reason string. + /// Hint: MQTT 5 feature only. + /// public string ReasonString { get; set; } } } diff --git a/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectedEventArgs.cs b/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectedEventArgs.cs index 5e2317cf..aa026342 100644 --- a/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectedEventArgs.cs +++ b/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectedEventArgs.cs @@ -15,12 +15,26 @@ namespace MQTTnet.Client.Disconnecting ReasonCode = reason; } + /// + /// Gets a value indicating whether the client was connected or not. + /// public bool ClientWasConnected { get; } + /// + /// Gets the exception. + /// public Exception Exception { get; } + /// + /// Gets the authentication result. + /// Hint: MQTT 5 feature only. + /// public MqttClientAuthenticateResult AuthenticateResult { get; } + /// + /// Gets or sets the reason. + /// Hint: MQTT 5 feature only. + /// public MqttClientDisconnectReason Reason { get; set; } [Obsolete("Please use 'Reason' instead. This property will be removed in the future!")] diff --git a/Source/MQTTnet/Client/ExtendedAuthenticationExchange/IMqttExtendedAuthenticationExchangeHandler.cs b/Source/MQTTnet/Client/ExtendedAuthenticationExchange/IMqttExtendedAuthenticationExchangeHandler.cs index 8cd35420..69a689cc 100644 --- a/Source/MQTTnet/Client/ExtendedAuthenticationExchange/IMqttExtendedAuthenticationExchangeHandler.cs +++ b/Source/MQTTnet/Client/ExtendedAuthenticationExchange/IMqttExtendedAuthenticationExchangeHandler.cs @@ -4,6 +4,11 @@ namespace MQTTnet.Client.ExtendedAuthenticationExchange { public interface IMqttExtendedAuthenticationExchangeHandler { + /// + /// Handles the authentication request. + /// + /// The context. + /// A representing any asynchronous operation. Task HandleRequestAsync(MqttExtendedAuthenticationExchangeContext context); } } diff --git a/Source/MQTTnet/Client/ExtendedAuthenticationExchange/MqttExtendedAuthenticationExchangeContext.cs b/Source/MQTTnet/Client/ExtendedAuthenticationExchange/MqttExtendedAuthenticationExchangeContext.cs index 9e88f039..a83576db 100644 --- a/Source/MQTTnet/Client/ExtendedAuthenticationExchange/MqttExtendedAuthenticationExchangeContext.cs +++ b/Source/MQTTnet/Client/ExtendedAuthenticationExchange/MqttExtendedAuthenticationExchangeContext.cs @@ -20,16 +20,42 @@ namespace MQTTnet.Client.ExtendedAuthenticationExchange Client = client ?? throw new ArgumentNullException(nameof(client)); } + /// + /// Gets the reason code. + /// Hint: MQTT 5 feature only. + /// public MqttAuthenticateReasonCode ReasonCode { get; } + /// + /// Gets the reason string. + /// Hint: MQTT 5 feature only. + /// public string ReasonString { get; } + /// + /// Gets the authentication method. + /// Hint: MQTT 5 feature only. + /// public string AuthenticationMethod { get; } + /// + /// Gets the authentication data. + /// Hint: MQTT 5 feature only. + /// public byte[] AuthenticationData { get; } + /// + /// Gets the user properties. + /// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet. + /// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber. + /// The feature is very similar to the HTTP header concept. + /// Hint: MQTT 5 feature only. + /// public List UserProperties { get; } + /// + /// Gets the client. + /// public IMqttClient Client { get; } } } diff --git a/Source/MQTTnet/Client/ExtendedAuthenticationExchange/MqttExtendedAuthenticationExchangeData.cs b/Source/MQTTnet/Client/ExtendedAuthenticationExchange/MqttExtendedAuthenticationExchangeData.cs index 8a49b08a..29f38217 100644 --- a/Source/MQTTnet/Client/ExtendedAuthenticationExchange/MqttExtendedAuthenticationExchangeData.cs +++ b/Source/MQTTnet/Client/ExtendedAuthenticationExchange/MqttExtendedAuthenticationExchangeData.cs @@ -6,12 +6,33 @@ namespace MQTTnet.Client.ExtendedAuthenticationExchange { public class MqttExtendedAuthenticationExchangeData { + /// + /// Gets or sets the reason code. + /// Hint: MQTT 5 feature only. + /// public MqttAuthenticateReasonCode ReasonCode { get; set; } + /// + /// Gets or sets the reason string. + /// Hint: MQTT 5 feature only. + /// public string ReasonString { get; set; } + /// + /// Gets or sets the authentication data. + /// Authentication data is binary information used to transmit multiple iterations of cryptographic secrets of protocol steps. + /// The content of the authentication data is highly dependent on the specific implementation of the authentication method. + /// Hint: MQTT 5 feature only. + /// public byte[] AuthenticationData { get; set; } + /// + /// Gets or sets the user properties. + /// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet. + /// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber. + /// The feature is very similar to the HTTP header concept. + /// Hint: MQTT 5 feature only. + /// public List UserProperties { get; } } } diff --git a/Source/MQTTnet/Client/IMqttClient.cs b/Source/MQTTnet/Client/IMqttClient.cs index 58cfa0c1..95ee1d92 100644 --- a/Source/MQTTnet/Client/IMqttClient.cs +++ b/Source/MQTTnet/Client/IMqttClient.cs @@ -12,19 +12,75 @@ namespace MQTTnet.Client { public interface IMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable { + /// + /// Gets a value indicating whether the client is connected or not. + /// bool IsConnected { get; } + + /// + /// Gets the options. + /// This contains all the set options for the client. + /// IMqttClientOptions Options { get; } + /// + /// Gets or sets the connected handler that is fired after the client has connected to the server successfully. + /// Hint: Initialize handlers before you connect the client to avoid issues. + /// IMqttClientConnectedHandler ConnectedHandler { get; set; } + /// + /// Gets or sets the disconnected handler that is fired after the client has disconnected from the server. + /// Hint: Initialize handlers before you connect the client to avoid issues. + /// IMqttClientDisconnectedHandler DisconnectedHandler { get; set; } + /// + /// Connects the client to the server using the specified options. + /// + /// The options that can be used on connect. + /// A cancellation token to stop the task. + /// A result containing the authentication result information. Task ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken); + + /// + /// Disconnects the client from the server. + /// + /// The options containing a disconnect reason. + /// A cancellation token to stop the task. + /// A representing any asynchronous operation. Task DisconnectAsync(MqttClientDisconnectOptions options, CancellationToken cancellationToken); + + /// + /// Pings the server. + /// + /// A cancellation token to stop the task. + /// A representing any asynchronous operation. Task PingAsync(CancellationToken cancellationToken); + /// + /// Sends extended authentication data. + /// Hint: MQTT 5 feature only. + /// + /// The extended data. + /// A cancellation token to stop the task. + /// A representing any asynchronous operation. Task SendExtendedAuthenticationExchangeDataAsync(MqttExtendedAuthenticationExchangeData data, CancellationToken cancellationToken); + + /// + /// Subscribes the client to topics. + /// + /// The subscription options. + /// A cancellation token to stop the task. + /// A representing any asynchronous operation. Task SubscribeAsync(MqttClientSubscribeOptions options, CancellationToken cancellationToken); + + /// + /// Unsubscribes the client from topics. + /// + /// The unsubscription options. + /// A cancellation token to stop the task. + /// A representing any asynchronous operation. Task UnsubscribeAsync(MqttClientUnsubscribeOptions options, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/Source/MQTTnet/Client/IMqttClientFactory.cs b/Source/MQTTnet/Client/IMqttClientFactory.cs index b256e742..54707fff 100644 --- a/Source/MQTTnet/Client/IMqttClientFactory.cs +++ b/Source/MQTTnet/Client/IMqttClientFactory.cs @@ -1,5 +1,4 @@ -using System; -using MQTTnet.Adapter; +using MQTTnet.Adapter; using MQTTnet.Diagnostics; using MQTTnet.LowLevelClient; @@ -7,22 +6,67 @@ namespace MQTTnet.Client { public interface IMqttClientFactory { + /// + /// A method to tell the client to use the adapter factory. + /// + /// The client adapter factory. + /// A new . IMqttFactory UseClientAdapterFactory(IMqttClientAdapterFactory clientAdapterFactory); + /// + /// Creates a new low level MQTT client. + /// + /// A new . ILowLevelMqttClient CreateLowLevelMqttClient(); + /// + /// Creates a new low level MQTT client. + /// + /// The logger. + /// A new . ILowLevelMqttClient CreateLowLevelMqttClient(IMqttNetLogger logger); + /// + /// Creates a new low level MQTT client. + /// + /// The client adapter factory. + /// A new . ILowLevelMqttClient CreateLowLevelMqttClient(IMqttClientAdapterFactory clientAdapterFactory); + /// + /// Creates a new low level MQTT client. + /// + /// The logger. + /// The client adapter factory. + /// A new . ILowLevelMqttClient CreateLowLevelMqttClient(IMqttNetLogger logger, IMqttClientAdapterFactory clientAdapterFactory); + /// + /// Creates a new MQTT client. + /// + /// A new . IMqttClient CreateMqttClient(); + /// + /// Creates a new MQTT client. + /// + /// The logger. + /// A new . IMqttClient CreateMqttClient(IMqttNetLogger logger); + /// + /// Creates a new MQTT client. + /// + /// The adapter factory. + /// A new . IMqttClient CreateMqttClient(IMqttClientAdapterFactory adapterFactory); + /// + /// Creates a new MQTT client. + /// + /// The logger. + /// The adapter factory. + /// A new . IMqttClient CreateMqttClient(IMqttNetLogger logger, IMqttClientAdapterFactory adapterFactory); } } \ No newline at end of file diff --git a/Source/MQTTnet/Client/Options/IMqttClientOptions.cs b/Source/MQTTnet/Client/Options/IMqttClientOptions.cs index e46162ae..642a8728 100644 --- a/Source/MQTTnet/Client/Options/IMqttClientOptions.cs +++ b/Source/MQTTnet/Client/Options/IMqttClientOptions.cs @@ -8,26 +8,122 @@ namespace MQTTnet.Client.Options { public interface IMqttClientOptions { + /// + /// Gets the client identifier. + /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. + /// string ClientId { get; } + + /// + /// Gets a value indicating whether clean sessions are used or not. + /// When a client connects to a broker it can connect using either a non persistent connection (clean session) or a persistent connection. + /// With a non persistent connection the broker doesn't store any subscription information or undelivered messages for the client. + /// This mode is ideal when the client only publishes messages. + /// It can also connect as a durable client using a persistent connection. + /// In this mode, the broker will store subscription information, and undelivered messages for the client. + /// bool CleanSession { get; } + + /// + /// Gets the credentials. + /// IMqttClientCredentials Credentials { get; } + + /// + /// Gets the extended authentication exchange handler. + /// IMqttExtendedAuthenticationExchangeHandler ExtendedAuthenticationExchangeHandler { get; } + + /// + /// Gets the protocol version. + /// MqttProtocolVersion ProtocolVersion { get; } + + /// + /// Gets the channel options. + /// IMqttClientChannelOptions ChannelOptions { get; } + /// + /// Gets the timeout the communication uses. + /// TimeSpan CommunicationTimeout { get; } + + /// + /// Gets the keep alive period. + /// The connection is normally left open by the client so that is can send and receive data at any time. + /// If no data flows over an open connection for a certain time period then the client will generate a PINGREQ and expect to receive a PINGRESP from the broker. + /// This message exchange confirms that the connection is open and working. + /// This period is known as the keep alive period. + /// TimeSpan KeepAlivePeriod { get; } + + /// + /// Gets the last will message. + /// In MQTT, you use the last will message feature to notify other clients about an ungracefully disconnected client. + /// MqttApplicationMessage WillMessage { get; } + + /// + /// Gets the will delay interval. + /// This is the time between the client disconnect and the time the will message will be sent. + /// uint? WillDelayInterval { get; } + /// + /// Gets the authentication method. + /// Hint: MQTT 5 feature only. + /// string AuthenticationMethod { get; } + + /// + /// Gets the authentication data. + /// Hint: MQTT 5 feature only. + /// byte[] AuthenticationData { get; } + + /// + /// Gets the maximum packet size. + /// uint? MaximumPacketSize { get; } + + /// + /// Gets the receive maximum. + /// This gives the maximum length of the receive messages. + /// ushort? ReceiveMaximum { get; } + + /// + /// Gets the request problem information. + /// Hint: MQTT 5 feature only. + /// bool? RequestProblemInformation { get; } + + /// + /// Gets the request response information. + /// Hint: MQTT 5 feature only. + /// bool? RequestResponseInformation { get; } + + /// + /// Gets the session expiry interval. + /// The time after a session expires when it's not actively used. + /// uint? SessionExpiryInterval { get; } + + /// + /// Gets the topic alias maximum. + /// This gives the maximum length of the topic alias. + /// ushort? TopicAliasMaximum { get; } + + /// + /// Gets or sets the user properties. + /// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet. + /// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber. + /// The feature is very similar to the HTTP header concept. + /// Hint: MQTT 5 feature only. + /// List UserProperties { get; set; } } } \ No newline at end of file diff --git a/Source/MQTTnet/Client/Options/MqttClientCertificateValidationCallbackContext.cs b/Source/MQTTnet/Client/Options/MqttClientCertificateValidationCallbackContext.cs index 3827f05d..0a863017 100644 --- a/Source/MQTTnet/Client/Options/MqttClientCertificateValidationCallbackContext.cs +++ b/Source/MQTTnet/Client/Options/MqttClientCertificateValidationCallbackContext.cs @@ -5,12 +5,24 @@ namespace MQTTnet.Client.Options { public class MqttClientCertificateValidationCallbackContext { + /// + /// Gets or sets the certificate. + /// public X509Certificate Certificate { get; set; } + /// + /// Gets or sets the certificate chain. + /// public X509Chain Chain { get; set; } + /// + /// Gets or sets the SSL policy errors. + /// public SslPolicyErrors SslPolicyErrors { get; set; } + /// + /// Gets or sets the client options. + /// public IMqttClientChannelOptions ClientOptions { get; set; } } } diff --git a/Source/MQTTnet/Client/Options/MqttClientOptions.cs b/Source/MQTTnet/Client/Options/MqttClientOptions.cs index 79c6c702..e150e496 100644 --- a/Source/MQTTnet/Client/Options/MqttClientOptions.cs +++ b/Source/MQTTnet/Client/Options/MqttClientOptions.cs @@ -8,28 +8,122 @@ namespace MQTTnet.Client.Options { public class MqttClientOptions : IMqttClientOptions { + /// + /// Gets the client identifier. + /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. + /// public string ClientId { get; set; } = Guid.NewGuid().ToString("N"); + + /// + /// Gets or sets a value indicating whether clean sessions are used or not. + /// When a client connects to a broker it can connect using either a non persistent connection (clean session) or a persistent connection. + /// With a non persistent connection the broker doesn't store any subscription information or undelivered messages for the client. + /// This mode is ideal when the client only publishes messages. + /// It can also connect as a durable client using a persistent connection. + /// In this mode, the broker will store subscription information, and undelivered messages for the client. + /// public bool CleanSession { get; set; } = true; + + /// + /// Gets or sets the credentials. + /// public IMqttClientCredentials Credentials { get; set; } + + /// + /// Gets or sets the extended authentication exchange handler. + /// public IMqttExtendedAuthenticationExchangeHandler ExtendedAuthenticationExchangeHandler { get; set; } + + /// + /// Gets or sets the protocol version. + /// public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311; + /// + /// Gets or sets the channel options. + /// public IMqttClientChannelOptions ChannelOptions { get; set; } + + /// + /// Gets or sets the timeout the communication uses. + /// public TimeSpan CommunicationTimeout { get; set; } = TimeSpan.FromSeconds(10); + + /// + /// Gets or sets the keep alive period. + /// The connection is normally left open by the client so that is can send and receive data at any time. + /// If no data flows over an open connection for a certain time period then the client will generate a PINGREQ and expect to receive a PINGRESP from the broker. + /// This message exchange confirms that the connection is open and working. + /// This period is known as the keep alive period. + /// public TimeSpan KeepAlivePeriod { get; set; } = TimeSpan.FromSeconds(15); + /// + /// Gets or sets the last will message. + /// In MQTT, you use the last will message feature to notify other clients about an ungracefully disconnected client. + /// public MqttApplicationMessage WillMessage { get; set; } + + /// + /// Gets or sets the will delay interval. + /// This is the time between the client disconnect and the time the will message will be sent. + /// public uint? WillDelayInterval { get; set; } + /// + /// Gets or sets the authentication method. + /// Hint: MQTT 5 feature only. + /// public string AuthenticationMethod { get; set; } + + /// + /// Gets or sets the authentication data. + /// Hint: MQTT 5 feature only. + /// public byte[] AuthenticationData { get; set; } + /// + /// Gets or sets the maximum packet size. + /// public uint? MaximumPacketSize { get; set; } + + /// + /// Gets or sets the receive maximum. + /// This gives the maximum length of the receive messages. + /// public ushort? ReceiveMaximum { get; set; } + + /// + /// Gets or sets the request problem information. + /// Hint: MQTT 5 feature only. + /// public bool? RequestProblemInformation { get; set; } + + /// + /// Gets or sets the request response information. + /// Hint: MQTT 5 feature only. + /// public bool? RequestResponseInformation { get; set; } + + /// + /// Gets or sets the session expiry interval. + /// The time after a session expires when it's not actively used. + /// public uint? SessionExpiryInterval { get; set; } + + /// + /// Gets or sets the topic alias maximum. + /// This gives the maximum length of the topic alias. + /// public ushort? TopicAliasMaximum { get; set; } + + /// + /// Gets or sets the user properties. + /// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet. + /// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber. + /// The feature is very similar to the HTTP header concept. + /// Hint: MQTT 5 feature only. + /// public List UserProperties { get; set; } } } diff --git a/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs b/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs index 68d1aa3f..402f28a1 100644 --- a/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs +++ b/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs @@ -17,6 +17,11 @@ namespace MQTTnet.Client.Options MqttClientOptionsBuilderTlsParameters _tlsParameters; MqttClientWebSocketProxyOptions _proxyOptions; + /// + /// Adds the protocol version to the MQTT client options. + /// + /// The protocol version. + /// A new instance of the class. public MqttClientOptionsBuilder WithProtocolVersion(MqttProtocolVersion value) { if (value == MqttProtocolVersion.Unknown) @@ -28,47 +33,88 @@ namespace MQTTnet.Client.Options return this; } + /// + /// Adds the communication timeout to the MQTT client options. + /// + /// The timeout. + /// A new instance of the class. public MqttClientOptionsBuilder WithCommunicationTimeout(TimeSpan value) { _options.CommunicationTimeout = value; return this; } + /// + /// Adds the clean session flag to the MQTT client options. + /// + /// The clean session flag. + /// A new instance of the class. public MqttClientOptionsBuilder WithCleanSession(bool value = true) { _options.CleanSession = value; return this; } + /// + /// Adds the keep alive send interval to the MQTT client options. + /// + /// The keep alive interval. + /// A new instance of the class. [Obsolete("This method is no longer supported. The client will send ping requests just before the keep alive interval is going to elapse. As per MQTT RFC the serve has to wait 1.5 times the interval so we don't need this anymore.")] public MqttClientOptionsBuilder WithKeepAliveSendInterval(TimeSpan value) { return this; } + /// + /// Adds no keep alive period to the MQTT client options. + /// + /// A new instance of the class. public MqttClientOptionsBuilder WithNoKeepAlive() { return WithKeepAlivePeriod(TimeSpan.Zero); } + /// + /// Adds the keep alive period to the MQTT client options. + /// + /// The keep alive period. + /// A new instance of the class. public MqttClientOptionsBuilder WithKeepAlivePeriod(TimeSpan value) { _options.KeepAlivePeriod = value; return this; } + /// + /// Adds the client identifier to the MQTT client options. + /// + /// The client identifier. + /// A new instance of the class. public MqttClientOptionsBuilder WithClientId(string value) { _options.ClientId = value; return this; } + /// + /// Adds the will message to the MQTT client options. + /// + /// The will message. + /// A new instance of the class. public MqttClientOptionsBuilder WithWillMessage(MqttApplicationMessage value) { _options.WillMessage = value; return this; } + /// + /// Adds the authentication to the MQTT client options. + /// Hint: MQTT 5 feature only. + /// + /// The authentication method. + /// The data. + /// A new instance of the class. public MqttClientOptionsBuilder WithAuthentication(string method, byte[] data) { _options.AuthenticationMethod = method; @@ -76,48 +122,92 @@ namespace MQTTnet.Client.Options return this; } + /// + /// Adds the will delay interval to the MQTT client options. + /// + /// The will delay interval. + /// A new instance of the class. public MqttClientOptionsBuilder WithWillDelayInterval(uint? willDelayInterval) { _options.WillDelayInterval = willDelayInterval; return this; } + /// + /// Adds the topic alias maximum to the MQTT client options. + /// + /// The topic alias maximum. + /// A new instance of the class. public MqttClientOptionsBuilder WithTopicAliasMaximum(ushort? topicAliasMaximum) { _options.TopicAliasMaximum = topicAliasMaximum; return this; } + /// + /// Adds the maximum packet size to the MQTT client options. + /// + /// The maximum packet size. + /// A new instance of the class. public MqttClientOptionsBuilder WithMaximumPacketSize(uint? maximumPacketSize) { _options.MaximumPacketSize = maximumPacketSize; return this; } + /// + /// Adds the receive maximum to the MQTT client options. + /// + /// The receive maximum. + /// A new instance of the class. public MqttClientOptionsBuilder WithReceiveMaximum(ushort? receiveMaximum) { _options.ReceiveMaximum = receiveMaximum; return this; } + /// + /// Adds the request problem information to the MQTT client options. + /// Hint: MQTT 5 feature only. + /// + /// The request problem information. + /// A new instance of the class. public MqttClientOptionsBuilder WithRequestProblemInformation(bool? requestProblemInformation = true) { _options.RequestProblemInformation = requestProblemInformation; return this; } + /// + /// Adds the request response information to the MQTT client options. + /// Hint: MQTT 5 feature only. + /// + /// The request response information. + /// A new instance of the class. public MqttClientOptionsBuilder WithRequestResponseInformation(bool? requestResponseInformation = true) { _options.RequestResponseInformation = requestResponseInformation; return this; } + /// + /// Adds the session expiry interval to the MQTT client options. + /// + /// The session expiry interval. + /// A new instance of the class. public MqttClientOptionsBuilder WithSessionExpiryInterval(uint? sessionExpiryInterval) { _options.SessionExpiryInterval = sessionExpiryInterval; return this; } + /// + /// Adds the user property to the MQTT client options. + /// Hint: MQTT 5 feature only. + /// + /// The user property name. + /// The user property value. + /// A new instance of the class. public MqttClientOptionsBuilder WithUserProperty(string name, string value) { if (name is null) throw new ArgumentNullException(nameof(name)); @@ -132,6 +222,12 @@ namespace MQTTnet.Client.Options return this; } + /// + /// Adds the credentials to the MQTT client options. + /// + /// The user name. + /// The password. + /// A new instance of the class. public MqttClientOptionsBuilder WithCredentials(string username, string password = null) { byte[] passwordBuffer = null; @@ -144,6 +240,12 @@ namespace MQTTnet.Client.Options return WithCredentials(username, passwordBuffer); } + /// + /// Adds the credentials to the MQTT client options. + /// + /// The user name. + /// The password. + /// A new instance of the class. public MqttClientOptionsBuilder WithCredentials(string username, byte[] password = null) { _options.Credentials = new MqttClientCredentials @@ -155,6 +257,11 @@ namespace MQTTnet.Client.Options return this; } + /// + /// Adds the credentials to the MQTT client options. + /// + /// The credentials. + /// A new instance of the class. public MqttClientOptionsBuilder WithCredentials(IMqttClientCredentials credentials) { _options.Credentials = credentials; @@ -162,12 +269,23 @@ namespace MQTTnet.Client.Options return this; } + /// + /// Adds a extended authentication exchange handler to the MQTT client options. + /// + /// The handler. + /// A new instance of the class. public MqttClientOptionsBuilder WithExtendedAuthenticationExchangeHandler(IMqttExtendedAuthenticationExchangeHandler handler) { _options.ExtendedAuthenticationExchangeHandler = handler; return this; } + /// + /// Adds a TCP server connection to the MQTT client options. + /// + /// The server. + /// The port. + /// A new instance of the class. public MqttClientOptionsBuilder WithTcpServer(string server, int? port = null) { _tcpOptions = new MqttClientTcpOptions @@ -179,6 +297,11 @@ namespace MQTTnet.Client.Options return this; } + /// + /// Adds a TCP server connection to the MQTT client options. + /// + /// The options builder. + /// A new instance of the class. // TODO: Consider creating _MqttClientTcpOptionsBuilder_ as overload. public MqttClientOptionsBuilder WithTcpServer(Action optionsBuilder) { @@ -190,6 +313,16 @@ namespace MQTTnet.Client.Options return this; } + /// + /// Adds a proxy connection to the MQTT client options. + /// + /// The address. + /// The user name. + /// The password. + /// The domain. + /// The bypass on local flag. + /// The bypass list. + /// A new instance of the class. public MqttClientOptionsBuilder WithProxy(string address, string username = null, string password = null, string domain = null, bool bypassOnLocal = false, string[] bypassList = null) { _proxyOptions = new MqttClientWebSocketProxyOptions @@ -205,6 +338,11 @@ namespace MQTTnet.Client.Options return this; } + /// + /// Adds a proxy connection to the MQTT client options. + /// + /// The options builder. + /// A new instance of the class. public MqttClientOptionsBuilder WithProxy(Action optionsBuilder) { if (optionsBuilder == null) throw new ArgumentNullException(nameof(optionsBuilder)); @@ -214,6 +352,12 @@ namespace MQTTnet.Client.Options return this; } + /// + /// Adds a web socket server to the MQTT client options. + /// + /// The uri. + /// The parameters. + /// A new instance of the class. public MqttClientOptionsBuilder WithWebSocketServer(string uri, MqttClientOptionsBuilderWebSocketParameters parameters = null) { _webSocketOptions = new MqttClientWebSocketOptions @@ -226,6 +370,11 @@ namespace MQTTnet.Client.Options return this; } + /// + /// Adds a web socket server to the MQTT client options. + /// + /// The options builder. + /// A new instance of the class. public MqttClientOptionsBuilder WithWebSocketServer(Action optionsBuilder) { if (optionsBuilder == null) throw new ArgumentNullException(nameof(optionsBuilder)); @@ -236,17 +385,31 @@ namespace MQTTnet.Client.Options return this; } + /// + /// Adds TLS to the MQTT client options. + /// + /// The parameters. + /// A new instance of the class. public MqttClientOptionsBuilder WithTls(MqttClientOptionsBuilderTlsParameters parameters) { _tlsParameters = parameters; return this; } + /// + /// Adds TLS to the MQTT client options. + /// + /// A new instance of the class. public MqttClientOptionsBuilder WithTls() { return WithTls(new MqttClientOptionsBuilderTlsParameters { UseTls = true }); } + /// + /// Adds TLS to the MQTT client options. + /// + /// The options builder. + /// A new instance of the class. public MqttClientOptionsBuilder WithTls(Action optionsBuilder) { if (optionsBuilder == null) throw new ArgumentNullException(nameof(optionsBuilder)); @@ -256,6 +419,10 @@ namespace MQTTnet.Client.Options return this; } + /// + /// Builds the MQTT client options. + /// + /// The . public IMqttClientOptions Build() { if (_tcpOptions == null && _webSocketOptions == null) diff --git a/Source/MQTTnet/Client/Publishing/MqttClientPublishResult.cs b/Source/MQTTnet/Client/Publishing/MqttClientPublishResult.cs index 15035ec0..0a51c989 100644 --- a/Source/MQTTnet/Client/Publishing/MqttClientPublishResult.cs +++ b/Source/MQTTnet/Client/Publishing/MqttClientPublishResult.cs @@ -6,12 +6,30 @@ namespace MQTTnet.Client.Publishing { public class MqttClientPublishResult { + /// + /// Gets or sets the packet identifier. + /// public ushort? PacketIdentifier { get; set; } + /// + /// Gets or sets the reason code. + /// Hint: MQTT 5 feature only. + /// public MqttClientPublishReasonCode ReasonCode { get; set; } = MqttClientPublishReasonCode.Success; + /// + /// Gets or sets the reason string. + /// Hint: MQTT 5 feature only. + /// public string ReasonString { get; set; } + /// + /// Gets or sets the user properties. + /// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet. + /// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber. + /// The feature is very similar to the HTTP header concept. + /// Hint: MQTT 5 feature only. + /// public List UserProperties { get; set; } } } diff --git a/Source/MQTTnet/Client/Receiving/IMqttApplicationMessageReceivedHandler.cs b/Source/MQTTnet/Client/Receiving/IMqttApplicationMessageReceivedHandler.cs index 3ea73175..199adba6 100644 --- a/Source/MQTTnet/Client/Receiving/IMqttApplicationMessageReceivedHandler.cs +++ b/Source/MQTTnet/Client/Receiving/IMqttApplicationMessageReceivedHandler.cs @@ -4,6 +4,11 @@ namespace MQTTnet.Client.Receiving { public interface IMqttApplicationMessageReceivedHandler { + /// + /// Handles the application message received event. + /// + /// The event args. + /// A representing any asynchronous operation. Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs); } } diff --git a/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeOptions.cs b/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeOptions.cs index 42c38e11..2c43c42a 100644 --- a/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeOptions.cs +++ b/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeOptions.cs @@ -5,10 +5,28 @@ namespace MQTTnet.Client.Subscribing { public class MqttClientSubscribeOptions { + /// + /// Gets or sets a list of topic filters the client wants to subscribe to. + /// Topic filters can include regular topics or wild cards. + /// public List TopicFilters { get; set; } = new List(); + /// + /// Gets or sets the subscription identifier. + /// The client can specify a subscription identifier when subscribing. + /// The broker will establish and store the mapping relationship between this subscription and subscription identifier when successfully create or modify subscription. + /// The broker will return the subscription identifier associated with this PUBLISH packet and the PUBLISH packet to the client when need to forward PUBLISH packets matching this subscription to this client. + /// Hint: MQTT 5 feature only. + /// public uint? SubscriptionIdentifier { get; set; } + /// + /// Gets or sets the user properties. + /// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet. + /// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber. + /// The feature is very similar to the HTTP header concept. + /// Hint: MQTT 5 feature only. + /// public List UserProperties { get; set; } } } diff --git a/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeOptionsBuilder.cs b/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeOptionsBuilder.cs index 97b5b776..96e6c5d1 100644 --- a/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeOptionsBuilder.cs +++ b/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeOptionsBuilder.cs @@ -9,6 +9,13 @@ namespace MQTTnet.Client.Subscribing { private readonly MqttClientSubscribeOptions _subscribeOptions = new MqttClientSubscribeOptions(); + /// + /// Adds the user property to the subscribe options. + /// Hint: MQTT 5 feature only. + /// + /// The property name. + /// The property value. + /// A new instance of the class. public MqttClientSubscribeOptionsBuilder WithUserProperty(string name, string value) { if (name == null) throw new ArgumentNullException(nameof(name)); @@ -24,13 +31,26 @@ namespace MQTTnet.Client.Subscribing return this; } + /// + /// Adds the subscription identifier to the subscribe options. + /// + /// The subscription identifier. + /// A new instance of the class. public MqttClientSubscribeOptionsBuilder WithSubscriptionIdentifier(uint? subscriptionIdentifier) { _subscribeOptions.SubscriptionIdentifier = subscriptionIdentifier; - return this; } + /// + /// Adds a topic filter to the subscribe options. + /// + /// The topic. + /// The quality of service level. + /// A value indicating whether to not send messages originating on this client (noLocal) or not. + /// A value indicating whether messages are retained as published or not. + /// The retain handling. + /// A new instance of the class. public MqttClientSubscribeOptionsBuilder WithTopicFilter( string topic, MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce, @@ -48,6 +68,11 @@ namespace MQTTnet.Client.Subscribing }); } + /// + /// Adds the topic filter to the subscribe options. + /// + /// The topic filter builder. + /// A new instance of the class. public MqttClientSubscribeOptionsBuilder WithTopicFilter(Action topicFilterBuilder) { if (topicFilterBuilder == null) throw new ArgumentNullException(nameof(topicFilterBuilder)); @@ -58,6 +83,11 @@ namespace MQTTnet.Client.Subscribing return WithTopicFilter(internalTopicFilterBuilder); } + /// + /// Adds the topic filter to the subscribe options. + /// + /// The topic filter builder. + /// A new instance of the class. public MqttClientSubscribeOptionsBuilder WithTopicFilter(MqttTopicFilterBuilder topicFilterBuilder) { if (topicFilterBuilder == null) throw new ArgumentNullException(nameof(topicFilterBuilder)); @@ -65,6 +95,11 @@ namespace MQTTnet.Client.Subscribing return WithTopicFilter(topicFilterBuilder.Build()); } + /// + /// Adds the topic filter to the subscribe options. + /// + /// The topic filter. + /// A new instance of the class. public MqttClientSubscribeOptionsBuilder WithTopicFilter(MqttTopicFilter topicFilter) { if (topicFilter == null) throw new ArgumentNullException(nameof(topicFilter)); @@ -79,6 +114,10 @@ namespace MQTTnet.Client.Subscribing return this; } + /// + /// Builds the subscription options. + /// + /// A new instance of the class. public MqttClientSubscribeOptions Build() { return _subscribeOptions; diff --git a/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResultItem.cs b/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResultItem.cs index 65f15c43..3e2d27f1 100644 --- a/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResultItem.cs +++ b/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResultItem.cs @@ -10,8 +10,16 @@ namespace MQTTnet.Client.Subscribing ResultCode = resultCode; } + /// + /// Gets or sets the topic filter. + /// The topic filter can contain topics and wildcards. + /// public MqttTopicFilter TopicFilter { get; } + /// + /// Gets or sets the result code. + /// Hint: MQTT 5 feature only. + /// public MqttClientSubscribeResultCode ResultCode { get; } } } diff --git a/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeOptions.cs b/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeOptions.cs index 73c21ef7..09a4ce29 100644 --- a/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeOptions.cs +++ b/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeOptions.cs @@ -5,8 +5,19 @@ namespace MQTTnet.Client.Unsubscribing { public class MqttClientUnsubscribeOptions { + /// + /// Gets or sets a list of topic filters the client wants to unsubscribe from. + /// Topic filters can include regular topics or wild cards. + /// public List TopicFilters { get; set; } = new List(); + /// + /// Gets or sets the user properties. + /// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet. + /// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber. + /// The feature is very similar to the HTTP header concept. + /// Hint: MQTT 5 feature only. + /// public List UserProperties { get; set; } = new List(); } } diff --git a/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeOptionsBuilder.cs b/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeOptionsBuilder.cs index 85162435..56a1997e 100644 --- a/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeOptionsBuilder.cs +++ b/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeOptionsBuilder.cs @@ -8,6 +8,13 @@ namespace MQTTnet.Client.Unsubscribing { private readonly MqttClientUnsubscribeOptions _unsubscribeOptions = new MqttClientUnsubscribeOptions(); + /// + /// Adds the user property to the unsubscribe options. + /// Hint: MQTT 5 feature only. + /// + /// The property name. + /// The property value. + /// A new instance of the class. public MqttClientUnsubscribeOptionsBuilder WithUserProperty(string name, string value) { if (name is null) throw new ArgumentNullException(nameof(name)); @@ -16,6 +23,12 @@ namespace MQTTnet.Client.Unsubscribing return WithUserProperty(new MqttUserProperty(name, value)); } + /// + /// Adds the user property to the unsubscribe options. + /// Hint: MQTT 5 feature only. + /// + /// The user property. + /// A new instance of the class. public MqttClientUnsubscribeOptionsBuilder WithUserProperty(MqttUserProperty userProperty) { if (userProperty is null) throw new ArgumentNullException(nameof(userProperty)); @@ -30,6 +43,11 @@ namespace MQTTnet.Client.Unsubscribing return this; } + /// + /// Adds the topic filter to the unsubscribe options. + /// + /// The topic. + /// A new instance of the class. public MqttClientUnsubscribeOptionsBuilder WithTopicFilter(string topic) { if (topic is null) throw new ArgumentNullException(nameof(topic)); @@ -44,6 +62,11 @@ namespace MQTTnet.Client.Unsubscribing return this; } + /// + /// Adds the topic filter to the unsubscribe options. + /// + /// The topic filter. + /// A new instance of the class. public MqttClientUnsubscribeOptionsBuilder WithTopicFilter(MqttTopicFilter topicFilter) { if (topicFilter is null) throw new ArgumentNullException(nameof(topicFilter)); @@ -51,6 +74,10 @@ namespace MQTTnet.Client.Unsubscribing return WithTopicFilter(topicFilter.Topic); } + /// + /// Builds the unsubscription options. + /// + /// A new instance of the class. public MqttClientUnsubscribeOptions Build() { return _unsubscribeOptions; diff --git a/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResultItem.cs b/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResultItem.cs index 755ee43c..065fb7f2 100644 --- a/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResultItem.cs +++ b/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResultItem.cs @@ -10,8 +10,16 @@ namespace MQTTnet.Client.Unsubscribing ReasonCode = reasonCode; } + /// + /// Gets or sets the topic filter. + /// The topic filter can contain topics and wildcards. + /// public string TopicFilter { get; } + /// + /// Gets or sets the result code. + /// Hint: MQTT 5 feature only. + /// public MqttClientUnsubscribeResultCode ReasonCode { get; } } } \ No newline at end of file diff --git a/Source/MQTTnet/IApplicationMessagePublisher.cs b/Source/MQTTnet/IApplicationMessagePublisher.cs index bf17718c..aa3caae7 100644 --- a/Source/MQTTnet/IApplicationMessagePublisher.cs +++ b/Source/MQTTnet/IApplicationMessagePublisher.cs @@ -6,6 +6,12 @@ namespace MQTTnet { public interface IApplicationMessagePublisher { + /// + /// Publishes a new application message to the server. + /// + /// The application message. + /// A cancellation token to stop the task. + /// A representing any asynchronous operation. Task PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken); } } diff --git a/Source/MQTTnet/IApplicationMessageReceiver.cs b/Source/MQTTnet/IApplicationMessageReceiver.cs index 4e474856..a44470d1 100644 --- a/Source/MQTTnet/IApplicationMessageReceiver.cs +++ b/Source/MQTTnet/IApplicationMessageReceiver.cs @@ -4,6 +4,10 @@ namespace MQTTnet { public interface IApplicationMessageReceiver { + /// + /// Gets or sets the application message received handler that is fired every time a new message is received on the client's subscriptions. + /// Hint: Initialize handlers before you connect the client to avoid issues. + /// IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler { get; set; } } } diff --git a/Source/MQTTnet/IMqttFactory.cs b/Source/MQTTnet/IMqttFactory.cs index 8c0e5af3..b68e27a9 100644 --- a/Source/MQTTnet/IMqttFactory.cs +++ b/Source/MQTTnet/IMqttFactory.cs @@ -7,8 +7,14 @@ namespace MQTTnet { public interface IMqttFactory : IMqttClientFactory, IMqttServerFactory { + /// + /// Gets the default logger. + /// IMqttNetLogger DefaultLogger { get; } + /// + /// Gets the properties. + /// IDictionary Properties { get; } } } diff --git a/Source/MQTTnet/MqttApplicationMessage.cs b/Source/MQTTnet/MqttApplicationMessage.cs index 6522d42f..99068fe5 100644 --- a/Source/MQTTnet/MqttApplicationMessage.cs +++ b/Source/MQTTnet/MqttApplicationMessage.cs @@ -6,28 +6,99 @@ namespace MQTTnet { public class MqttApplicationMessage { + /// + /// Gets or sets the MQTT topic. + /// In MQTT, the word topic refers to an UTF-8 string that the broker uses to filter messages for each connected client. + /// The topic consists of one or more topic levels. Each topic level is separated by a forward slash (topic level separator). + /// public string Topic { get; set; } + /// + /// Gets or sets the payload. + /// The payload is the data bytes sent via the MQTT protocol. + /// public byte[] Payload { get; set; } + /// + /// Gets or sets the quality of service level. + /// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message. + /// There are 3 QoS levels in MQTT: + /// - At most once (0): Message gets delivered no time, once or multiple times. + /// - At least once (1): Message gets delivered at least once (one time or more often). + /// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once). + /// public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; } + /// + /// Gets or sets a value indicating whether the message should be retained or not. + /// A retained message is a normal MQTT message with the retained flag set to true. + /// The broker stores the last retained message and the corresponding QoS for that topic. + /// public bool Retain { get; set; } + /// + /// Gets or sets the user properties. + /// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet. + /// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber. + /// The feature is very similar to the HTTP header concept. + /// Hint: MQTT 5 feature only. + /// public List UserProperties { get; set; } + /// + /// Gets or sets the content type. + /// The content type must be a UTF-8 encoded string. The content type value identifies the kind of UTF-8 encoded payload. + /// public string ContentType { get; set; } + /// + /// Gets or sets the response topic. + /// In MQTT 5 the ability to publish a response topic was added in the publish message which allows you to implement the request/response pattern between clients that is common in web applications. + /// Hint: MQTT 5 feature only. + /// public string ResponseTopic { get; set; } + /// + /// Gets or sets the payload format indicator. + /// The payload format indicator is part of any MQTT packet that can contain a payload. The indicator is an optional byte value. + /// A value of 0 indicates an “unspecified byte stream”. + /// A value of 1 indicates a "UTF-8 encoded payload". + /// If no payload format indicator is provided, the default value is 0. + /// Hint: MQTT 5 feature only. + /// public MqttPayloadFormatIndicator? PayloadFormatIndicator { get; set; } + /// + /// Gets or sets the message expiry interval. + /// A client can set the message expiry interval in seconds for each PUBLISH message individually. + /// This interval defines the period of time that the broker stores the PUBLISH message for any matching subscribers that are not currently connected. + /// When no message expiry interval is set, the broker must store the message for matching subscribers indefinitely. + /// When the retained=true option is set on the PUBLISH message, this interval also defines how long a message is retained on a topic. + /// Hint: MQTT 5 feature only. + /// public uint? MessageExpiryInterval { get; set; } + /// + /// Gets or sets the topic alias. + /// Topic aliases were introduced are a mechanism for reducing the size of published packets by reducing the size of the topic field. + /// Hint: MQTT 5 feature only. + /// public ushort? TopicAlias { get; set; } + /// + /// Gets or sets the correlation data. + /// In order for the sender to know what sent message the response refers to it can also send correlation data with the published message. + /// Hint: MQTT 5 feature only. + /// public byte[] CorrelationData { get; set; } + /// + /// Gets or sets the subscription identifiers. + /// The client can specify a subscription identifier when subscribing. + /// The broker will establish and store the mapping relationship between this subscription and subscription identifier when successfully create or modify subscription. + /// The broker will return the subscription identifier associated with this PUBLISH packet and the PUBLISH packet to the client when need to forward PUBLISH packets matching this subscription to this client. + /// Hint: MQTT 5 feature only. + /// public List SubscriptionIdentifiers { get; set; } } } diff --git a/Source/MQTTnet/MqttApplicationMessageBuilder.cs b/Source/MQTTnet/MqttApplicationMessageBuilder.cs index 7e48531f..e03d2597 100644 --- a/Source/MQTTnet/MqttApplicationMessageBuilder.cs +++ b/Source/MQTTnet/MqttApplicationMessageBuilder.cs @@ -11,31 +11,128 @@ namespace MQTTnet { public class MqttApplicationMessageBuilder { + /// + /// The quality of service level. + /// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message. + /// There are 3 QoS levels in MQTT: + /// - At most once (0): Message gets delivered no time, once or multiple times. + /// - At least once (1): Message gets delivered at least once (one time or more often). + /// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once). + /// private MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce; + + /// + /// The MQTT topic. + /// In MQTT, the word topic refers to an UTF-8 string that the broker uses to filter messages for each connected client. + /// The topic consists of one or more topic levels. Each topic level is separated by a forward slash (topic level separator). + /// private string _topic; + + /// + /// The payload. + /// The payload is the data bytes sent via the MQTT protocol. + /// private byte[] _payload; + + /// + /// A value indicating whether the message should be retained or not. + /// A retained message is a normal MQTT message with the retained flag set to true. + /// The broker stores the last retained message and the corresponding QoS for that topic. + /// private bool _retain; + + /// + /// The content type. + /// The content type must be a UTF-8 encoded string. The content type value identifies the kind of UTF-8 encoded payload. + /// private string _contentType; + + /// + /// The response topic. + /// In MQTT 5 the ability to publish a response topic was added in the publish message which allows you to implement the request/response pattern between clients that is common in web applications. + /// Hint: MQTT 5 feature only. + /// private string _responseTopic; + + /// + /// The correlation data. + /// In order for the sender to know what sent message the response refers to it can also send correlation data with the published message. + /// Hint: MQTT 5 feature only. + /// private byte[] _correlationData; + + /// + /// The topic alias. + /// Topic aliases were introduced are a mechanism for reducing the size of published packets by reducing the size of the topic field. + /// Hint: MQTT 5 feature only. + /// private ushort? _topicAlias; + + /// + /// The subscription identifiers. + /// The client can specify a subscription identifier when subscribing. + /// The broker will establish and store the mapping relationship between this subscription and subscription identifier when successfully create or modify subscription. + /// The broker will return the subscription identifier associated with this PUBLISH packet and the PUBLISH packet to the client when need to forward PUBLISH packets matching this subscription to this client. + /// Hint: MQTT 5 feature only. + /// private List _subscriptionIdentifiers; + + /// + /// The message expiry interval. + /// A client can set the message expiry interval in seconds for each PUBLISH message individually. + /// This interval defines the period of time that the broker stores the PUBLISH message for any matching subscribers that are not currently connected. + /// When no message expiry interval is set, the broker must store the message for matching subscribers indefinitely. + /// When the retained=true option is set on the PUBLISH message, this interval also defines how long a message is retained on a topic. + /// Hint: MQTT 5 feature only. + /// private uint? _messageExpiryInterval; + + /// + /// The payload format indicator. + /// The payload format indicator is part of any MQTT packet that can contain a payload. The indicator is an optional byte value. + /// A value of 0 indicates an “unspecified byte stream”. + /// A value of 1 indicates a "UTF-8 encoded payload". + /// If no payload format indicator is provided, the default value is 0. + /// Hint: MQTT 5 feature only. + /// private MqttPayloadFormatIndicator? _payloadFormatIndicator; + + /// + /// The user properties. + /// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet. + /// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber. + /// The feature is very similar to the HTTP header concept. + /// + /// Hint: MQTT 5 feature only. private List _userProperties; + /// + /// Adds a topic to the message. + /// + /// The topic. + /// A new instance of the class. public MqttApplicationMessageBuilder WithTopic(string topic) { _topic = topic; return this; } + /// + /// Adds a payload to the message. + /// + /// The payload. + /// A new instance of the class. public MqttApplicationMessageBuilder WithPayload(byte[] payload) { _payload = payload; return this; } + /// + /// Adds a payload to the message. + /// + /// The payload. + /// A new instance of the class. public MqttApplicationMessageBuilder WithPayload(IEnumerable payload) { if (payload == null) @@ -54,6 +151,11 @@ namespace MQTTnet return this; } + /// + /// Adds a payload to the message. + /// + /// The payload. + /// A new instance of the class. public MqttApplicationMessageBuilder WithPayload(Stream payload) { if (payload == null) @@ -65,6 +167,12 @@ namespace MQTTnet return WithPayload(payload, payload.Length - payload.Position); } + /// + /// Adds a payload to the message. + /// + /// The payload. + /// The stream length. + /// A new instance of the class. public MqttApplicationMessageBuilder WithPayload(Stream payload, long length) { if (payload == null) @@ -86,6 +194,11 @@ namespace MQTTnet return this; } + /// + /// Adds a payload to the message. + /// + /// The payload. + /// A new instance of the class. public MqttApplicationMessageBuilder WithPayload(string payload) { if (payload == null) @@ -98,12 +211,22 @@ namespace MQTTnet return this; } + /// + /// Adds the quality of service level to the message. + /// + /// The quality of service level. + /// A new instance of the class. public MqttApplicationMessageBuilder WithQualityOfServiceLevel(MqttQualityOfServiceLevel qualityOfServiceLevel) { _qualityOfServiceLevel = qualityOfServiceLevel; return this; } + /// + /// Adds the quality of service level to the message. + /// + /// The quality of service level. + /// A new instance of the class. public MqttApplicationMessageBuilder WithQualityOfServiceLevel(int qualityOfServiceLevel) { if (qualityOfServiceLevel < 0 || qualityOfServiceLevel > 2) @@ -114,24 +237,40 @@ namespace MQTTnet return WithQualityOfServiceLevel((MqttQualityOfServiceLevel)qualityOfServiceLevel); } + /// + /// Adds retain flag to the message. + /// + /// A new instance of the class. public MqttApplicationMessageBuilder WithRetainFlag(bool value = true) { _retain = value; return this; } + /// + /// Adds the quality of service level 0 (at least once) to the message. + /// + /// A new instance of the class. public MqttApplicationMessageBuilder WithAtLeastOnceQoS() { _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce; return this; } + /// + /// Adds the quality of service level 1 (at most once) to the message. + /// + /// A new instance of the class. public MqttApplicationMessageBuilder WithAtMostOnceQoS() { _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce; return this; } + /// + /// Adds the quality of service level 2 (exactly once) to the message. + /// + /// A new instance of the class. public MqttApplicationMessageBuilder WithExactlyOnceQoS() { _qualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce; @@ -139,8 +278,12 @@ namespace MQTTnet } /// - /// This is only supported when using MQTTv5. + /// Adds the user property to the message. + /// Hint: MQTT 5 feature only. /// + /// The property name. + /// The property value. + /// A new instance of the class. public MqttApplicationMessageBuilder WithUserProperty(string name, string value) { if (_userProperties == null) @@ -153,8 +296,10 @@ namespace MQTTnet } /// - /// This is only supported when using MQTTv5. + /// Adds the content type to the message. + /// Hint: MQTT 5 feature only. /// + /// A new instance of the class. public MqttApplicationMessageBuilder WithContentType(string contentType) { _contentType = contentType; @@ -162,8 +307,11 @@ namespace MQTTnet } /// - /// This is only supported when using MQTTv5. + /// Adds the response topic to the message. + /// Hint: MQTT 5 feature only. /// + /// The response topic. + /// A new instance of the class. public MqttApplicationMessageBuilder WithResponseTopic(string responseTopic) { _responseTopic = responseTopic; @@ -171,8 +319,11 @@ namespace MQTTnet } /// - /// This is only supported when using MQTTv5. + /// Adds the correlation data to the message. + /// Hint: MQTT 5 feature only. /// + /// The correlation data. + /// A new instance of the class. public MqttApplicationMessageBuilder WithCorrelationData(byte[] correlationData) { _correlationData = correlationData; @@ -180,8 +331,11 @@ namespace MQTTnet } /// - /// This is only supported when using MQTTv5. + /// Adds the topic alias to the message. + /// Hint: MQTT 5 feature only. /// + /// The topic alias. + /// A new instance of the class. public MqttApplicationMessageBuilder WithTopicAlias(ushort topicAlias) { _topicAlias = topicAlias; @@ -189,8 +343,11 @@ namespace MQTTnet } /// - /// This is only supported when using MQTTv5. + /// Adds the subscription identifier to the message. + /// Hint: MQTT 5 feature only. /// + /// The subscription identifier. + /// A new instance of the class. public MqttApplicationMessageBuilder WithSubscriptionIdentifier(uint subscriptionIdentifier) { if (_subscriptionIdentifiers == null) @@ -203,8 +360,11 @@ namespace MQTTnet } /// - /// This is only supported when using MQTTv5. + /// Adds the message expiry interval to the message. + /// Hint: MQTT 5 feature only. /// + /// The message expiry interval. + /// A new instance of the class. public MqttApplicationMessageBuilder WithMessageExpiryInterval(uint messageExpiryInterval) { _messageExpiryInterval = messageExpiryInterval; @@ -212,14 +372,21 @@ namespace MQTTnet } /// - /// This is only supported when using MQTTv5. + /// Adds the payload format indicator to the message. + /// Hint: MQTT 5 feature only. /// + /// The payload format indicator. + /// A new instance of the class. public MqttApplicationMessageBuilder WithPayloadFormatIndicator(MqttPayloadFormatIndicator payloadFormatIndicator) { _payloadFormatIndicator = payloadFormatIndicator; return this; } + /// + /// Builds the message. + /// + /// A new instance of the class. public MqttApplicationMessage Build() { if (!_topicAlias.HasValue && string.IsNullOrEmpty(_topic)) diff --git a/Source/MQTTnet/MqttApplicationMessageExtensions.cs b/Source/MQTTnet/MqttApplicationMessageExtensions.cs index dac9998a..26191240 100644 --- a/Source/MQTTnet/MqttApplicationMessageExtensions.cs +++ b/Source/MQTTnet/MqttApplicationMessageExtensions.cs @@ -5,6 +5,11 @@ namespace MQTTnet { public static class MqttApplicationMessageExtensions { + /// + /// Converts the payload to a string if possible. + /// + /// The application message. + /// A string. public static string ConvertPayloadToString(this MqttApplicationMessage applicationMessage) { if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage)); diff --git a/Source/MQTTnet/MqttTopicFilter.cs b/Source/MQTTnet/MqttTopicFilter.cs index 66ea94a4..fda60e0a 100644 --- a/Source/MQTTnet/MqttTopicFilter.cs +++ b/Source/MQTTnet/MqttTopicFilter.cs @@ -10,22 +10,38 @@ namespace MQTTnet public class MqttTopicFilter { + /// + /// Gets or sets the MQTT topic. + /// In MQTT, the word topic refers to an UTF-8 string that the broker uses to filter messages for each connected client. + /// The topic consists of one or more topic levels. Each topic level is separated by a forward slash (topic level separator). + /// public string Topic { get; set; } + /// + /// Gets or sets the quality of service level. + /// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message. + /// There are 3 QoS levels in MQTT: + /// - At most once (0): Message gets delivered no time, once or multiple times. + /// - At least once (1): Message gets delivered at least once (one time or more often). + /// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once). + /// public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; } /// - /// This is only supported when using MQTTv5. + /// Gets or sets a value indicating whether to not send messages originating on this client (noLocal) or not. /// + /// Hint: MQTT 5 feature only. public bool? NoLocal { get; set; } /// - /// This is only supported when using MQTTv5. + /// Gets or sets a value indicating whether messages are retained as published or not. + /// Hint: MQTT 5 feature only. /// public bool? RetainAsPublished { get; set; } /// - /// This is only supported when using MQTTv5. + /// Gets or sets the retain handling. + /// Hint: MQTT 5 feature only. /// public MqttRetainHandling? RetainHandling { get; set; } diff --git a/Source/MQTTnet/MqttTopicFilterBuilder.cs b/Source/MQTTnet/MqttTopicFilterBuilder.cs index 712f25da..bcf6efd2 100644 --- a/Source/MQTTnet/MqttTopicFilterBuilder.cs +++ b/Source/MQTTnet/MqttTopicFilterBuilder.cs @@ -11,39 +11,79 @@ namespace MQTTnet public class MqttTopicFilterBuilder { + /// + /// The quality of service level. + /// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message. + /// There are 3 QoS levels in MQTT: + /// - At most once (0): Message gets delivered no time, once or multiple times. + /// - At least once (1): Message gets delivered at least once (one time or more often). + /// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once). + /// MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce; + + /// + /// The MQTT topic. + /// In MQTT, the word topic refers to an UTF-8 string that the broker uses to filter messages for each connected client. + /// The topic consists of one or more topic levels. Each topic level is separated by a forward slash (topic level separator). + /// string _topic; + /// + /// Adds a topic to the topic filter. + /// + /// The topic. + /// A new instance of the class. public MqttTopicFilterBuilder WithTopic(string topic) { _topic = topic; return this; } + /// + /// Adds the quality of service level to the topic filter. + /// + /// The quality of service level. + /// A new instance of the class. public MqttTopicFilterBuilder WithQualityOfServiceLevel(MqttQualityOfServiceLevel qualityOfServiceLevel) { _qualityOfServiceLevel = qualityOfServiceLevel; return this; } + /// + /// Adds the quality of service level 0 (at least once) to the topic filter. + /// + /// A new instance of the class. public MqttTopicFilterBuilder WithAtLeastOnceQoS() { _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce; return this; } + /// + /// Adds the quality of service level 1 (at most once) to the topic filter. + /// + /// A new instance of the class. public MqttTopicFilterBuilder WithAtMostOnceQoS() { _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce; return this; } + /// + /// Adds the quality of service level 2 (exactly once) to the topic filter. + /// + /// A new instance of the class. public MqttTopicFilterBuilder WithExactlyOnceQoS() { _qualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce; return this; } + /// + /// Builds the topic filter. + /// + /// A new instance of the class. public MqttTopicFilter Build() { if (string.IsNullOrEmpty(_topic)) diff --git a/Source/MQTTnet/Server/IMqttServerFactory.cs b/Source/MQTTnet/Server/IMqttServerFactory.cs index 39efaa3d..05090bb7 100644 --- a/Source/MQTTnet/Server/IMqttServerFactory.cs +++ b/Source/MQTTnet/Server/IMqttServerFactory.cs @@ -7,14 +7,37 @@ namespace MQTTnet.Server { public interface IMqttServerFactory { + /// + /// Gets the default server adapters. + /// IList> DefaultServerAdapters { get; } + /// + /// Creates a new default MQTT server. + /// + /// A new . IMqttServer CreateMqttServer(); + /// + /// Creates a new default MQTT server. + /// + /// The logger. + /// A new . IMqttServer CreateMqttServer(IMqttNetLogger logger); + /// + /// Creates a new default MQTT server. + /// + /// The adapters. + /// A new . IMqttServer CreateMqttServer(IEnumerable adapters); + /// + /// Creates a new default MQTT server. + /// + /// The adapters. + /// The logger. + /// A new . IMqttServer CreateMqttServer(IEnumerable adapters, IMqttNetLogger logger); } } \ No newline at end of file From 5eab337e13d909e462d3d6a7b17f64133ddc7d47 Mon Sep 17 00:00:00 2001 From: SeppPenner Date: Fri, 15 Jan 2021 19:41:45 +0100 Subject: [PATCH 02/29] Removed not needed comments on client side (Server side is still missing). --- .../Connecting/IMqttClientConnectedHandler.cs | 5 - .../MqttClientAuthenticateResult.cs | 33 ---- .../IMqttClientDisconnectedHandler.cs | 5 - .../MqttClientDisconnectedEventArgs.cs | 6 - ...ttExtendedAuthenticationExchangeHandler.cs | 5 - ...ttExtendedAuthenticationExchangeContext.cs | 3 - Source/MQTTnet/Client/IMqttClient.cs | 32 ---- Source/MQTTnet/Client/IMqttClientFactory.cs | 45 ----- .../Client/Options/IMqttClientOptions.cs | 18 -- ...entCertificateValidationCallbackContext.cs | 12 -- .../Client/Options/MqttClientOptions.cs | 18 -- .../Options/MqttClientOptionsBuilder.cs | 167 ------------------ .../Publishing/MqttClientPublishResult.cs | 3 - .../IMqttApplicationMessageReceivedHandler.cs | 5 - .../MqttClientSubscribeOptionsBuilder.cs | 33 ---- .../MqttClientUnsubscribeOptionsBuilder.cs | 14 -- 16 files changed, 404 deletions(-) diff --git a/Source/MQTTnet/Client/Connecting/IMqttClientConnectedHandler.cs b/Source/MQTTnet/Client/Connecting/IMqttClientConnectedHandler.cs index 233ee577..8b7be27e 100644 --- a/Source/MQTTnet/Client/Connecting/IMqttClientConnectedHandler.cs +++ b/Source/MQTTnet/Client/Connecting/IMqttClientConnectedHandler.cs @@ -4,11 +4,6 @@ namespace MQTTnet.Client.Connecting { public interface IMqttClientConnectedHandler { - /// - /// Handles the events when a client gets connected. - /// - /// The event args. - /// A representing any asynchronous operation. Task HandleConnectedAsync(MqttClientConnectedEventArgs eventArgs); } } diff --git a/Source/MQTTnet/Client/Connecting/MqttClientAuthenticateResult.cs b/Source/MQTTnet/Client/Connecting/MqttClientAuthenticateResult.cs index dfcaaff1..ecb1293c 100644 --- a/Source/MQTTnet/Client/Connecting/MqttClientAuthenticateResult.cs +++ b/Source/MQTTnet/Client/Connecting/MqttClientAuthenticateResult.cs @@ -13,24 +13,12 @@ namespace MQTTnet.Client.Connecting /// public MqttClientConnectResultCode ResultCode { get; set; } - /// - /// Gets or sets a value indicating whether the session is present or not. - /// public bool IsSessionPresent { get; set; } - /// - /// Gets or sets a value indicating whether wildcard subscriptions are available. - /// public bool? WildcardSubscriptionAvailable { get; set; } - /// - /// Gets or sets a value indicating whether retained messages are available. - /// public bool? RetainAvailable { get; set; } - /// - /// Gets or sets the assigned client identifier. - /// public string AssignedClientIdentifier { get; set; } /// @@ -45,9 +33,6 @@ namespace MQTTnet.Client.Connecting /// public byte[] AuthenticationData { get; set; } - /// - /// Gets or sets the maximum packet size. - /// public uint? MaximumPacketSize { get; set; } /// @@ -56,14 +41,8 @@ namespace MQTTnet.Client.Connecting /// public string ReasonString { get; set; } - /// - /// Gets or sets the receive maximum. - /// public ushort? ReceiveMaximum { get; set; } - /// - /// Gets or sets the maximum quality od service level. - /// public MqttQualityOfServiceLevel MaximumQoS { get; set; } /// @@ -72,24 +51,12 @@ namespace MQTTnet.Client.Connecting /// public string ResponseInformation { get; set; } - /// - /// Gets or sets the topic alias maximum. - /// public ushort? TopicAliasMaximum { get; set; } - /// - /// Gets or sets the server reference. - /// public string ServerReference { get; set; } - /// - /// Gets or sets the server keep alive. - /// public ushort? ServerKeepAlive { get; set; } - /// - /// Gets or sets the session expiry interval. - /// public uint? SessionExpiryInterval { get; set; } /// diff --git a/Source/MQTTnet/Client/Disconnecting/IMqttClientDisconnectedHandler.cs b/Source/MQTTnet/Client/Disconnecting/IMqttClientDisconnectedHandler.cs index 81e0b9ba..14347bf0 100644 --- a/Source/MQTTnet/Client/Disconnecting/IMqttClientDisconnectedHandler.cs +++ b/Source/MQTTnet/Client/Disconnecting/IMqttClientDisconnectedHandler.cs @@ -4,11 +4,6 @@ namespace MQTTnet.Client.Disconnecting { public interface IMqttClientDisconnectedHandler { - /// - /// Handles the events when a client gets disconnected. - /// - /// The event args. - /// A representing any asynchronous operation. Task HandleDisconnectedAsync(MqttClientDisconnectedEventArgs eventArgs); } } diff --git a/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectedEventArgs.cs b/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectedEventArgs.cs index aa026342..932f009c 100644 --- a/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectedEventArgs.cs +++ b/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectedEventArgs.cs @@ -15,14 +15,8 @@ namespace MQTTnet.Client.Disconnecting ReasonCode = reason; } - /// - /// Gets a value indicating whether the client was connected or not. - /// public bool ClientWasConnected { get; } - /// - /// Gets the exception. - /// public Exception Exception { get; } /// diff --git a/Source/MQTTnet/Client/ExtendedAuthenticationExchange/IMqttExtendedAuthenticationExchangeHandler.cs b/Source/MQTTnet/Client/ExtendedAuthenticationExchange/IMqttExtendedAuthenticationExchangeHandler.cs index 69a689cc..8cd35420 100644 --- a/Source/MQTTnet/Client/ExtendedAuthenticationExchange/IMqttExtendedAuthenticationExchangeHandler.cs +++ b/Source/MQTTnet/Client/ExtendedAuthenticationExchange/IMqttExtendedAuthenticationExchangeHandler.cs @@ -4,11 +4,6 @@ namespace MQTTnet.Client.ExtendedAuthenticationExchange { public interface IMqttExtendedAuthenticationExchangeHandler { - /// - /// Handles the authentication request. - /// - /// The context. - /// A representing any asynchronous operation. Task HandleRequestAsync(MqttExtendedAuthenticationExchangeContext context); } } diff --git a/Source/MQTTnet/Client/ExtendedAuthenticationExchange/MqttExtendedAuthenticationExchangeContext.cs b/Source/MQTTnet/Client/ExtendedAuthenticationExchange/MqttExtendedAuthenticationExchangeContext.cs index a83576db..082e9a8d 100644 --- a/Source/MQTTnet/Client/ExtendedAuthenticationExchange/MqttExtendedAuthenticationExchangeContext.cs +++ b/Source/MQTTnet/Client/ExtendedAuthenticationExchange/MqttExtendedAuthenticationExchangeContext.cs @@ -53,9 +53,6 @@ namespace MQTTnet.Client.ExtendedAuthenticationExchange /// public List UserProperties { get; } - /// - /// Gets the client. - /// public IMqttClient Client { get; } } } diff --git a/Source/MQTTnet/Client/IMqttClient.cs b/Source/MQTTnet/Client/IMqttClient.cs index 95ee1d92..dbe005a0 100644 --- a/Source/MQTTnet/Client/IMqttClient.cs +++ b/Source/MQTTnet/Client/IMqttClient.cs @@ -12,9 +12,6 @@ namespace MQTTnet.Client { public interface IMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable { - /// - /// Gets a value indicating whether the client is connected or not. - /// bool IsConnected { get; } /// @@ -35,27 +32,10 @@ namespace MQTTnet.Client /// IMqttClientDisconnectedHandler DisconnectedHandler { get; set; } - /// - /// Connects the client to the server using the specified options. - /// - /// The options that can be used on connect. - /// A cancellation token to stop the task. - /// A result containing the authentication result information. Task ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken); - /// - /// Disconnects the client from the server. - /// - /// The options containing a disconnect reason. - /// A cancellation token to stop the task. - /// A representing any asynchronous operation. Task DisconnectAsync(MqttClientDisconnectOptions options, CancellationToken cancellationToken); - /// - /// Pings the server. - /// - /// A cancellation token to stop the task. - /// A representing any asynchronous operation. Task PingAsync(CancellationToken cancellationToken); /// @@ -67,20 +47,8 @@ namespace MQTTnet.Client /// A representing any asynchronous operation. Task SendExtendedAuthenticationExchangeDataAsync(MqttExtendedAuthenticationExchangeData data, CancellationToken cancellationToken); - /// - /// Subscribes the client to topics. - /// - /// The subscription options. - /// A cancellation token to stop the task. - /// A representing any asynchronous operation. Task SubscribeAsync(MqttClientSubscribeOptions options, CancellationToken cancellationToken); - /// - /// Unsubscribes the client from topics. - /// - /// The unsubscription options. - /// A cancellation token to stop the task. - /// A representing any asynchronous operation. Task UnsubscribeAsync(MqttClientUnsubscribeOptions options, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/Source/MQTTnet/Client/IMqttClientFactory.cs b/Source/MQTTnet/Client/IMqttClientFactory.cs index 54707fff..4bbb79a3 100644 --- a/Source/MQTTnet/Client/IMqttClientFactory.cs +++ b/Source/MQTTnet/Client/IMqttClientFactory.cs @@ -6,67 +6,22 @@ namespace MQTTnet.Client { public interface IMqttClientFactory { - /// - /// A method to tell the client to use the adapter factory. - /// - /// The client adapter factory. - /// A new . IMqttFactory UseClientAdapterFactory(IMqttClientAdapterFactory clientAdapterFactory); - /// - /// Creates a new low level MQTT client. - /// - /// A new . ILowLevelMqttClient CreateLowLevelMqttClient(); - /// - /// Creates a new low level MQTT client. - /// - /// The logger. - /// A new . ILowLevelMqttClient CreateLowLevelMqttClient(IMqttNetLogger logger); - /// - /// Creates a new low level MQTT client. - /// - /// The client adapter factory. - /// A new . ILowLevelMqttClient CreateLowLevelMqttClient(IMqttClientAdapterFactory clientAdapterFactory); - /// - /// Creates a new low level MQTT client. - /// - /// The logger. - /// The client adapter factory. - /// A new . ILowLevelMqttClient CreateLowLevelMqttClient(IMqttNetLogger logger, IMqttClientAdapterFactory clientAdapterFactory); - /// - /// Creates a new MQTT client. - /// - /// A new . IMqttClient CreateMqttClient(); - /// - /// Creates a new MQTT client. - /// - /// The logger. - /// A new . IMqttClient CreateMqttClient(IMqttNetLogger logger); - /// - /// Creates a new MQTT client. - /// - /// The adapter factory. - /// A new . IMqttClient CreateMqttClient(IMqttClientAdapterFactory adapterFactory); - /// - /// Creates a new MQTT client. - /// - /// The logger. - /// The adapter factory. - /// A new . IMqttClient CreateMqttClient(IMqttNetLogger logger, IMqttClientAdapterFactory adapterFactory); } } \ No newline at end of file diff --git a/Source/MQTTnet/Client/Options/IMqttClientOptions.cs b/Source/MQTTnet/Client/Options/IMqttClientOptions.cs index 642a8728..e159d998 100644 --- a/Source/MQTTnet/Client/Options/IMqttClientOptions.cs +++ b/Source/MQTTnet/Client/Options/IMqttClientOptions.cs @@ -24,29 +24,14 @@ namespace MQTTnet.Client.Options /// bool CleanSession { get; } - /// - /// Gets the credentials. - /// IMqttClientCredentials Credentials { get; } - /// - /// Gets the extended authentication exchange handler. - /// IMqttExtendedAuthenticationExchangeHandler ExtendedAuthenticationExchangeHandler { get; } - /// - /// Gets the protocol version. - /// MqttProtocolVersion ProtocolVersion { get; } - /// - /// Gets the channel options. - /// IMqttClientChannelOptions ChannelOptions { get; } - /// - /// Gets the timeout the communication uses. - /// TimeSpan CommunicationTimeout { get; } /// @@ -82,9 +67,6 @@ namespace MQTTnet.Client.Options /// byte[] AuthenticationData { get; } - /// - /// Gets the maximum packet size. - /// uint? MaximumPacketSize { get; } /// diff --git a/Source/MQTTnet/Client/Options/MqttClientCertificateValidationCallbackContext.cs b/Source/MQTTnet/Client/Options/MqttClientCertificateValidationCallbackContext.cs index 0a863017..3827f05d 100644 --- a/Source/MQTTnet/Client/Options/MqttClientCertificateValidationCallbackContext.cs +++ b/Source/MQTTnet/Client/Options/MqttClientCertificateValidationCallbackContext.cs @@ -5,24 +5,12 @@ namespace MQTTnet.Client.Options { public class MqttClientCertificateValidationCallbackContext { - /// - /// Gets or sets the certificate. - /// public X509Certificate Certificate { get; set; } - /// - /// Gets or sets the certificate chain. - /// public X509Chain Chain { get; set; } - /// - /// Gets or sets the SSL policy errors. - /// public SslPolicyErrors SslPolicyErrors { get; set; } - /// - /// Gets or sets the client options. - /// public IMqttClientChannelOptions ClientOptions { get; set; } } } diff --git a/Source/MQTTnet/Client/Options/MqttClientOptions.cs b/Source/MQTTnet/Client/Options/MqttClientOptions.cs index e150e496..6332a03b 100644 --- a/Source/MQTTnet/Client/Options/MqttClientOptions.cs +++ b/Source/MQTTnet/Client/Options/MqttClientOptions.cs @@ -24,29 +24,14 @@ namespace MQTTnet.Client.Options /// public bool CleanSession { get; set; } = true; - /// - /// Gets or sets the credentials. - /// public IMqttClientCredentials Credentials { get; set; } - /// - /// Gets or sets the extended authentication exchange handler. - /// public IMqttExtendedAuthenticationExchangeHandler ExtendedAuthenticationExchangeHandler { get; set; } - /// - /// Gets or sets the protocol version. - /// public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311; - /// - /// Gets or sets the channel options. - /// public IMqttClientChannelOptions ChannelOptions { get; set; } - /// - /// Gets or sets the timeout the communication uses. - /// public TimeSpan CommunicationTimeout { get; set; } = TimeSpan.FromSeconds(10); /// @@ -82,9 +67,6 @@ namespace MQTTnet.Client.Options /// public byte[] AuthenticationData { get; set; } - /// - /// Gets or sets the maximum packet size. - /// public uint? MaximumPacketSize { get; set; } /// diff --git a/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs b/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs index 402f28a1..68d1aa3f 100644 --- a/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs +++ b/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs @@ -17,11 +17,6 @@ namespace MQTTnet.Client.Options MqttClientOptionsBuilderTlsParameters _tlsParameters; MqttClientWebSocketProxyOptions _proxyOptions; - /// - /// Adds the protocol version to the MQTT client options. - /// - /// The protocol version. - /// A new instance of the class. public MqttClientOptionsBuilder WithProtocolVersion(MqttProtocolVersion value) { if (value == MqttProtocolVersion.Unknown) @@ -33,88 +28,47 @@ namespace MQTTnet.Client.Options return this; } - /// - /// Adds the communication timeout to the MQTT client options. - /// - /// The timeout. - /// A new instance of the class. public MqttClientOptionsBuilder WithCommunicationTimeout(TimeSpan value) { _options.CommunicationTimeout = value; return this; } - /// - /// Adds the clean session flag to the MQTT client options. - /// - /// The clean session flag. - /// A new instance of the class. public MqttClientOptionsBuilder WithCleanSession(bool value = true) { _options.CleanSession = value; return this; } - /// - /// Adds the keep alive send interval to the MQTT client options. - /// - /// The keep alive interval. - /// A new instance of the class. [Obsolete("This method is no longer supported. The client will send ping requests just before the keep alive interval is going to elapse. As per MQTT RFC the serve has to wait 1.5 times the interval so we don't need this anymore.")] public MqttClientOptionsBuilder WithKeepAliveSendInterval(TimeSpan value) { return this; } - /// - /// Adds no keep alive period to the MQTT client options. - /// - /// A new instance of the class. public MqttClientOptionsBuilder WithNoKeepAlive() { return WithKeepAlivePeriod(TimeSpan.Zero); } - /// - /// Adds the keep alive period to the MQTT client options. - /// - /// The keep alive period. - /// A new instance of the class. public MqttClientOptionsBuilder WithKeepAlivePeriod(TimeSpan value) { _options.KeepAlivePeriod = value; return this; } - /// - /// Adds the client identifier to the MQTT client options. - /// - /// The client identifier. - /// A new instance of the class. public MqttClientOptionsBuilder WithClientId(string value) { _options.ClientId = value; return this; } - /// - /// Adds the will message to the MQTT client options. - /// - /// The will message. - /// A new instance of the class. public MqttClientOptionsBuilder WithWillMessage(MqttApplicationMessage value) { _options.WillMessage = value; return this; } - /// - /// Adds the authentication to the MQTT client options. - /// Hint: MQTT 5 feature only. - /// - /// The authentication method. - /// The data. - /// A new instance of the class. public MqttClientOptionsBuilder WithAuthentication(string method, byte[] data) { _options.AuthenticationMethod = method; @@ -122,92 +76,48 @@ namespace MQTTnet.Client.Options return this; } - /// - /// Adds the will delay interval to the MQTT client options. - /// - /// The will delay interval. - /// A new instance of the class. public MqttClientOptionsBuilder WithWillDelayInterval(uint? willDelayInterval) { _options.WillDelayInterval = willDelayInterval; return this; } - /// - /// Adds the topic alias maximum to the MQTT client options. - /// - /// The topic alias maximum. - /// A new instance of the class. public MqttClientOptionsBuilder WithTopicAliasMaximum(ushort? topicAliasMaximum) { _options.TopicAliasMaximum = topicAliasMaximum; return this; } - /// - /// Adds the maximum packet size to the MQTT client options. - /// - /// The maximum packet size. - /// A new instance of the class. public MqttClientOptionsBuilder WithMaximumPacketSize(uint? maximumPacketSize) { _options.MaximumPacketSize = maximumPacketSize; return this; } - /// - /// Adds the receive maximum to the MQTT client options. - /// - /// The receive maximum. - /// A new instance of the class. public MqttClientOptionsBuilder WithReceiveMaximum(ushort? receiveMaximum) { _options.ReceiveMaximum = receiveMaximum; return this; } - /// - /// Adds the request problem information to the MQTT client options. - /// Hint: MQTT 5 feature only. - /// - /// The request problem information. - /// A new instance of the class. public MqttClientOptionsBuilder WithRequestProblemInformation(bool? requestProblemInformation = true) { _options.RequestProblemInformation = requestProblemInformation; return this; } - /// - /// Adds the request response information to the MQTT client options. - /// Hint: MQTT 5 feature only. - /// - /// The request response information. - /// A new instance of the class. public MqttClientOptionsBuilder WithRequestResponseInformation(bool? requestResponseInformation = true) { _options.RequestResponseInformation = requestResponseInformation; return this; } - /// - /// Adds the session expiry interval to the MQTT client options. - /// - /// The session expiry interval. - /// A new instance of the class. public MqttClientOptionsBuilder WithSessionExpiryInterval(uint? sessionExpiryInterval) { _options.SessionExpiryInterval = sessionExpiryInterval; return this; } - /// - /// Adds the user property to the MQTT client options. - /// Hint: MQTT 5 feature only. - /// - /// The user property name. - /// The user property value. - /// A new instance of the class. public MqttClientOptionsBuilder WithUserProperty(string name, string value) { if (name is null) throw new ArgumentNullException(nameof(name)); @@ -222,12 +132,6 @@ namespace MQTTnet.Client.Options return this; } - /// - /// Adds the credentials to the MQTT client options. - /// - /// The user name. - /// The password. - /// A new instance of the class. public MqttClientOptionsBuilder WithCredentials(string username, string password = null) { byte[] passwordBuffer = null; @@ -240,12 +144,6 @@ namespace MQTTnet.Client.Options return WithCredentials(username, passwordBuffer); } - /// - /// Adds the credentials to the MQTT client options. - /// - /// The user name. - /// The password. - /// A new instance of the class. public MqttClientOptionsBuilder WithCredentials(string username, byte[] password = null) { _options.Credentials = new MqttClientCredentials @@ -257,11 +155,6 @@ namespace MQTTnet.Client.Options return this; } - /// - /// Adds the credentials to the MQTT client options. - /// - /// The credentials. - /// A new instance of the class. public MqttClientOptionsBuilder WithCredentials(IMqttClientCredentials credentials) { _options.Credentials = credentials; @@ -269,23 +162,12 @@ namespace MQTTnet.Client.Options return this; } - /// - /// Adds a extended authentication exchange handler to the MQTT client options. - /// - /// The handler. - /// A new instance of the class. public MqttClientOptionsBuilder WithExtendedAuthenticationExchangeHandler(IMqttExtendedAuthenticationExchangeHandler handler) { _options.ExtendedAuthenticationExchangeHandler = handler; return this; } - /// - /// Adds a TCP server connection to the MQTT client options. - /// - /// The server. - /// The port. - /// A new instance of the class. public MqttClientOptionsBuilder WithTcpServer(string server, int? port = null) { _tcpOptions = new MqttClientTcpOptions @@ -297,11 +179,6 @@ namespace MQTTnet.Client.Options return this; } - /// - /// Adds a TCP server connection to the MQTT client options. - /// - /// The options builder. - /// A new instance of the class. // TODO: Consider creating _MqttClientTcpOptionsBuilder_ as overload. public MqttClientOptionsBuilder WithTcpServer(Action optionsBuilder) { @@ -313,16 +190,6 @@ namespace MQTTnet.Client.Options return this; } - /// - /// Adds a proxy connection to the MQTT client options. - /// - /// The address. - /// The user name. - /// The password. - /// The domain. - /// The bypass on local flag. - /// The bypass list. - /// A new instance of the class. public MqttClientOptionsBuilder WithProxy(string address, string username = null, string password = null, string domain = null, bool bypassOnLocal = false, string[] bypassList = null) { _proxyOptions = new MqttClientWebSocketProxyOptions @@ -338,11 +205,6 @@ namespace MQTTnet.Client.Options return this; } - /// - /// Adds a proxy connection to the MQTT client options. - /// - /// The options builder. - /// A new instance of the class. public MqttClientOptionsBuilder WithProxy(Action optionsBuilder) { if (optionsBuilder == null) throw new ArgumentNullException(nameof(optionsBuilder)); @@ -352,12 +214,6 @@ namespace MQTTnet.Client.Options return this; } - /// - /// Adds a web socket server to the MQTT client options. - /// - /// The uri. - /// The parameters. - /// A new instance of the class. public MqttClientOptionsBuilder WithWebSocketServer(string uri, MqttClientOptionsBuilderWebSocketParameters parameters = null) { _webSocketOptions = new MqttClientWebSocketOptions @@ -370,11 +226,6 @@ namespace MQTTnet.Client.Options return this; } - /// - /// Adds a web socket server to the MQTT client options. - /// - /// The options builder. - /// A new instance of the class. public MqttClientOptionsBuilder WithWebSocketServer(Action optionsBuilder) { if (optionsBuilder == null) throw new ArgumentNullException(nameof(optionsBuilder)); @@ -385,31 +236,17 @@ namespace MQTTnet.Client.Options return this; } - /// - /// Adds TLS to the MQTT client options. - /// - /// The parameters. - /// A new instance of the class. public MqttClientOptionsBuilder WithTls(MqttClientOptionsBuilderTlsParameters parameters) { _tlsParameters = parameters; return this; } - /// - /// Adds TLS to the MQTT client options. - /// - /// A new instance of the class. public MqttClientOptionsBuilder WithTls() { return WithTls(new MqttClientOptionsBuilderTlsParameters { UseTls = true }); } - /// - /// Adds TLS to the MQTT client options. - /// - /// The options builder. - /// A new instance of the class. public MqttClientOptionsBuilder WithTls(Action optionsBuilder) { if (optionsBuilder == null) throw new ArgumentNullException(nameof(optionsBuilder)); @@ -419,10 +256,6 @@ namespace MQTTnet.Client.Options return this; } - /// - /// Builds the MQTT client options. - /// - /// The . public IMqttClientOptions Build() { if (_tcpOptions == null && _webSocketOptions == null) diff --git a/Source/MQTTnet/Client/Publishing/MqttClientPublishResult.cs b/Source/MQTTnet/Client/Publishing/MqttClientPublishResult.cs index 0a51c989..429f130a 100644 --- a/Source/MQTTnet/Client/Publishing/MqttClientPublishResult.cs +++ b/Source/MQTTnet/Client/Publishing/MqttClientPublishResult.cs @@ -6,9 +6,6 @@ namespace MQTTnet.Client.Publishing { public class MqttClientPublishResult { - /// - /// Gets or sets the packet identifier. - /// public ushort? PacketIdentifier { get; set; } /// diff --git a/Source/MQTTnet/Client/Receiving/IMqttApplicationMessageReceivedHandler.cs b/Source/MQTTnet/Client/Receiving/IMqttApplicationMessageReceivedHandler.cs index 199adba6..3ea73175 100644 --- a/Source/MQTTnet/Client/Receiving/IMqttApplicationMessageReceivedHandler.cs +++ b/Source/MQTTnet/Client/Receiving/IMqttApplicationMessageReceivedHandler.cs @@ -4,11 +4,6 @@ namespace MQTTnet.Client.Receiving { public interface IMqttApplicationMessageReceivedHandler { - /// - /// Handles the application message received event. - /// - /// The event args. - /// A representing any asynchronous operation. Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs); } } diff --git a/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeOptionsBuilder.cs b/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeOptionsBuilder.cs index 96e6c5d1..14d2ad6e 100644 --- a/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeOptionsBuilder.cs +++ b/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeOptionsBuilder.cs @@ -31,26 +31,12 @@ namespace MQTTnet.Client.Subscribing return this; } - /// - /// Adds the subscription identifier to the subscribe options. - /// - /// The subscription identifier. - /// A new instance of the class. public MqttClientSubscribeOptionsBuilder WithSubscriptionIdentifier(uint? subscriptionIdentifier) { _subscribeOptions.SubscriptionIdentifier = subscriptionIdentifier; return this; } - /// - /// Adds a topic filter to the subscribe options. - /// - /// The topic. - /// The quality of service level. - /// A value indicating whether to not send messages originating on this client (noLocal) or not. - /// A value indicating whether messages are retained as published or not. - /// The retain handling. - /// A new instance of the class. public MqttClientSubscribeOptionsBuilder WithTopicFilter( string topic, MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce, @@ -68,11 +54,6 @@ namespace MQTTnet.Client.Subscribing }); } - /// - /// Adds the topic filter to the subscribe options. - /// - /// The topic filter builder. - /// A new instance of the class. public MqttClientSubscribeOptionsBuilder WithTopicFilter(Action topicFilterBuilder) { if (topicFilterBuilder == null) throw new ArgumentNullException(nameof(topicFilterBuilder)); @@ -83,11 +64,6 @@ namespace MQTTnet.Client.Subscribing return WithTopicFilter(internalTopicFilterBuilder); } - /// - /// Adds the topic filter to the subscribe options. - /// - /// The topic filter builder. - /// A new instance of the class. public MqttClientSubscribeOptionsBuilder WithTopicFilter(MqttTopicFilterBuilder topicFilterBuilder) { if (topicFilterBuilder == null) throw new ArgumentNullException(nameof(topicFilterBuilder)); @@ -95,11 +71,6 @@ namespace MQTTnet.Client.Subscribing return WithTopicFilter(topicFilterBuilder.Build()); } - /// - /// Adds the topic filter to the subscribe options. - /// - /// The topic filter. - /// A new instance of the class. public MqttClientSubscribeOptionsBuilder WithTopicFilter(MqttTopicFilter topicFilter) { if (topicFilter == null) throw new ArgumentNullException(nameof(topicFilter)); @@ -114,10 +85,6 @@ namespace MQTTnet.Client.Subscribing return this; } - /// - /// Builds the subscription options. - /// - /// A new instance of the class. public MqttClientSubscribeOptions Build() { return _subscribeOptions; diff --git a/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeOptionsBuilder.cs b/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeOptionsBuilder.cs index 56a1997e..a7f022e1 100644 --- a/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeOptionsBuilder.cs +++ b/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeOptionsBuilder.cs @@ -43,11 +43,6 @@ namespace MQTTnet.Client.Unsubscribing return this; } - /// - /// Adds the topic filter to the unsubscribe options. - /// - /// The topic. - /// A new instance of the class. public MqttClientUnsubscribeOptionsBuilder WithTopicFilter(string topic) { if (topic is null) throw new ArgumentNullException(nameof(topic)); @@ -62,11 +57,6 @@ namespace MQTTnet.Client.Unsubscribing return this; } - /// - /// Adds the topic filter to the unsubscribe options. - /// - /// The topic filter. - /// A new instance of the class. public MqttClientUnsubscribeOptionsBuilder WithTopicFilter(MqttTopicFilter topicFilter) { if (topicFilter is null) throw new ArgumentNullException(nameof(topicFilter)); @@ -74,10 +64,6 @@ namespace MQTTnet.Client.Unsubscribing return WithTopicFilter(topicFilter.Topic); } - /// - /// Builds the unsubscription options. - /// - /// A new instance of the class. public MqttClientUnsubscribeOptions Build() { return _unsubscribeOptions; From bba04e2b5ddb5b832d8360295f23dc2390f52010 Mon Sep 17 00:00:00 2001 From: SeppPenner Date: Fri, 15 Jan 2021 20:34:33 +0100 Subject: [PATCH 03/29] Updated all code comments, should be ok now. --- .../IManagedMqttClient.cs | 59 -------------- .../TopicGeneration/TopicGenerationContext.cs | 8 ++ .../MQTTnet/IApplicationMessagePublisher.cs | 6 -- Source/MQTTnet/IMqttFactory.cs | 6 -- .../MQTTnet/MqttApplicationMessageBuilder.cs | 61 --------------- .../MqttApplicationMessageExtensions.cs | 5 -- ...MqttApplicationMessageReceivedEventArgs.cs | 4 + Source/MQTTnet/MqttTopicFilterBuilder.cs | 26 ------- .../Server/CheckSubscriptionsResult.cs | 8 ++ Source/MQTTnet/Server/IMqttClientSession.cs | 4 + Source/MQTTnet/Server/IMqttServerFactory.cs | 23 ------ Source/MQTTnet/Server/IMqttServerOptions.cs | 4 + .../Server/IMqttServerPersistedSession.cs | 12 +++ ...qttApplicationMessageInterceptorContext.cs | 4 + ...qttClientMessageQueueInterceptorContext.cs | 8 ++ .../Server/MqttConnectionValidatorContext.cs | 76 ++++++++++++++++++- .../Server/MqttQueuedApplicationMessage.cs | 8 ++ .../MqttServerClientConnectedEventArgs.cs | 4 + .../MqttServerClientDisconnectedEventArgs.cs | 6 +- ...qttServerClientSubscribedTopicEventArgs.cs | 8 ++ ...tServerClientUnsubscribedTopicEventArgs.cs | 8 ++ Source/MQTTnet/Server/MqttServerOptions.cs | 4 + .../MqttSubscriptionInterceptorContext.cs | 8 ++ .../MqttUnsubscriptionInterceptorContext.cs | 9 +++ .../Server/Status/IMqttClientStatus.cs | 4 + .../Server/Status/IMqttSessionStatus.cs | 4 + .../MQTTnet/Server/Status/MqttClientStatus.cs | 4 + .../Server/Status/MqttSessionStatus.cs | 4 + 28 files changed, 197 insertions(+), 188 deletions(-) diff --git a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs b/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs index 4e06eaf9..720ed127 100644 --- a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs +++ b/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs @@ -16,95 +16,36 @@ namespace MQTTnet.Extensions.ManagedClient /// IMqttClient InternalClient { get; } - /// - /// Gets a value indicating whether the client is started or not. - /// bool IsStarted { get; } - /// - /// Gets a value indicating whether the client is connected or not. - /// bool IsConnected { get; } - /// - /// Gets a pending application message count. - /// int PendingApplicationMessagesCount { get; } - /// - /// Gets the MQTT client options. - /// IManagedMqttClientOptions Options { get; } - /// - /// Gets or sets the connected handler. - /// IMqttClientConnectedHandler ConnectedHandler { get; set; } - /// - /// Gets or sets the disconnected handler. - /// IMqttClientDisconnectedHandler DisconnectedHandler { get; set; } - /// - /// Gets or sets the application message process handler. - /// IApplicationMessageProcessedHandler ApplicationMessageProcessedHandler { get; set; } - /// - /// Gets or sets the application message skipped handler. - /// IApplicationMessageSkippedHandler ApplicationMessageSkippedHandler { get; set; } - /// - /// Gets or sets the connecting failed handler. - /// IConnectingFailedHandler ConnectingFailedHandler { get; set; } - /// - /// Gets or sets the synchronizing subscriptions failed handler. - /// ISynchronizingSubscriptionsFailedHandler SynchronizingSubscriptionsFailedHandler { get; set; } - /// - /// Starts the client. - /// - /// The options. - /// A representing any asynchronous operation. Task StartAsync(IManagedMqttClientOptions options); - /// - /// Stops the client. - /// - /// A representing any asynchronous operation. Task StopAsync(); - /// - /// Pings the server. - /// - /// A token to cancel the task. - /// A representing any asynchronous operation. Task PingAsync(CancellationToken cancellationToken); - /// - /// Subscribes the client. - /// - /// The topic filters. - /// A representing any asynchronous operation. Task SubscribeAsync(IEnumerable topicFilters); - /// - /// Unsubscribes the client. - /// - /// The topics. - /// A representing any asynchronous operation. Task UnsubscribeAsync(IEnumerable topics); - /// - /// Publishes a message from the client. - /// - /// The application messages. - /// A representing any asynchronous operation. Task PublishAsync(ManagedMqttApplicationMessage applicationMessages); } } \ No newline at end of file diff --git a/Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/TopicGenerationContext.cs b/Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/TopicGenerationContext.cs index cd82c52a..39110bb4 100644 --- a/Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/TopicGenerationContext.cs +++ b/Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/TopicGenerationContext.cs @@ -7,6 +7,14 @@ namespace MQTTnet.Extensions.Rpc.Options.TopicGeneration { public string MethodName { get; set; } + /// + /// Gets or sets the quality of service level. + /// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message. + /// There are 3 QoS levels in MQTT: + /// - At most once (0): Message gets delivered no time, once or multiple times. + /// - At least once (1): Message gets delivered at least once (one time or more often). + /// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once). + /// public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; } public IMqttClient MqttClient { get; set; } diff --git a/Source/MQTTnet/IApplicationMessagePublisher.cs b/Source/MQTTnet/IApplicationMessagePublisher.cs index aa3caae7..bf17718c 100644 --- a/Source/MQTTnet/IApplicationMessagePublisher.cs +++ b/Source/MQTTnet/IApplicationMessagePublisher.cs @@ -6,12 +6,6 @@ namespace MQTTnet { public interface IApplicationMessagePublisher { - /// - /// Publishes a new application message to the server. - /// - /// The application message. - /// A cancellation token to stop the task. - /// A representing any asynchronous operation. Task PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken); } } diff --git a/Source/MQTTnet/IMqttFactory.cs b/Source/MQTTnet/IMqttFactory.cs index b68e27a9..8c0e5af3 100644 --- a/Source/MQTTnet/IMqttFactory.cs +++ b/Source/MQTTnet/IMqttFactory.cs @@ -7,14 +7,8 @@ namespace MQTTnet { public interface IMqttFactory : IMqttClientFactory, IMqttServerFactory { - /// - /// Gets the default logger. - /// IMqttNetLogger DefaultLogger { get; } - /// - /// Gets the properties. - /// IDictionary Properties { get; } } } diff --git a/Source/MQTTnet/MqttApplicationMessageBuilder.cs b/Source/MQTTnet/MqttApplicationMessageBuilder.cs index e03d2597..f2fc0cd4 100644 --- a/Source/MQTTnet/MqttApplicationMessageBuilder.cs +++ b/Source/MQTTnet/MqttApplicationMessageBuilder.cs @@ -106,33 +106,18 @@ namespace MQTTnet /// Hint: MQTT 5 feature only. private List _userProperties; - /// - /// Adds a topic to the message. - /// - /// The topic. - /// A new instance of the class. public MqttApplicationMessageBuilder WithTopic(string topic) { _topic = topic; return this; } - /// - /// Adds a payload to the message. - /// - /// The payload. - /// A new instance of the class. public MqttApplicationMessageBuilder WithPayload(byte[] payload) { _payload = payload; return this; } - /// - /// Adds a payload to the message. - /// - /// The payload. - /// A new instance of the class. public MqttApplicationMessageBuilder WithPayload(IEnumerable payload) { if (payload == null) @@ -151,11 +136,6 @@ namespace MQTTnet return this; } - /// - /// Adds a payload to the message. - /// - /// The payload. - /// A new instance of the class. public MqttApplicationMessageBuilder WithPayload(Stream payload) { if (payload == null) @@ -167,12 +147,6 @@ namespace MQTTnet return WithPayload(payload, payload.Length - payload.Position); } - /// - /// Adds a payload to the message. - /// - /// The payload. - /// The stream length. - /// A new instance of the class. public MqttApplicationMessageBuilder WithPayload(Stream payload, long length) { if (payload == null) @@ -194,11 +168,6 @@ namespace MQTTnet return this; } - /// - /// Adds a payload to the message. - /// - /// The payload. - /// A new instance of the class. public MqttApplicationMessageBuilder WithPayload(string payload) { if (payload == null) @@ -211,22 +180,12 @@ namespace MQTTnet return this; } - /// - /// Adds the quality of service level to the message. - /// - /// The quality of service level. - /// A new instance of the class. public MqttApplicationMessageBuilder WithQualityOfServiceLevel(MqttQualityOfServiceLevel qualityOfServiceLevel) { _qualityOfServiceLevel = qualityOfServiceLevel; return this; } - /// - /// Adds the quality of service level to the message. - /// - /// The quality of service level. - /// A new instance of the class. public MqttApplicationMessageBuilder WithQualityOfServiceLevel(int qualityOfServiceLevel) { if (qualityOfServiceLevel < 0 || qualityOfServiceLevel > 2) @@ -237,40 +196,24 @@ namespace MQTTnet return WithQualityOfServiceLevel((MqttQualityOfServiceLevel)qualityOfServiceLevel); } - /// - /// Adds retain flag to the message. - /// - /// A new instance of the class. public MqttApplicationMessageBuilder WithRetainFlag(bool value = true) { _retain = value; return this; } - /// - /// Adds the quality of service level 0 (at least once) to the message. - /// - /// A new instance of the class. public MqttApplicationMessageBuilder WithAtLeastOnceQoS() { _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce; return this; } - /// - /// Adds the quality of service level 1 (at most once) to the message. - /// - /// A new instance of the class. public MqttApplicationMessageBuilder WithAtMostOnceQoS() { _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce; return this; } - /// - /// Adds the quality of service level 2 (exactly once) to the message. - /// - /// A new instance of the class. public MqttApplicationMessageBuilder WithExactlyOnceQoS() { _qualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce; @@ -383,10 +326,6 @@ namespace MQTTnet return this; } - /// - /// Builds the message. - /// - /// A new instance of the class. public MqttApplicationMessage Build() { if (!_topicAlias.HasValue && string.IsNullOrEmpty(_topic)) diff --git a/Source/MQTTnet/MqttApplicationMessageExtensions.cs b/Source/MQTTnet/MqttApplicationMessageExtensions.cs index 26191240..dac9998a 100644 --- a/Source/MQTTnet/MqttApplicationMessageExtensions.cs +++ b/Source/MQTTnet/MqttApplicationMessageExtensions.cs @@ -5,11 +5,6 @@ namespace MQTTnet { public static class MqttApplicationMessageExtensions { - /// - /// Converts the payload to a string if possible. - /// - /// The application message. - /// A string. public static string ConvertPayloadToString(this MqttApplicationMessage applicationMessage) { if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage)); diff --git a/Source/MQTTnet/MqttApplicationMessageReceivedEventArgs.cs b/Source/MQTTnet/MqttApplicationMessageReceivedEventArgs.cs index ed46305c..ac307de4 100644 --- a/Source/MQTTnet/MqttApplicationMessageReceivedEventArgs.cs +++ b/Source/MQTTnet/MqttApplicationMessageReceivedEventArgs.cs @@ -10,6 +10,10 @@ namespace MQTTnet ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage)); } + /// + /// Gets the client identifier. + /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. + /// public string ClientId { get; } public MqttApplicationMessage ApplicationMessage { get; } diff --git a/Source/MQTTnet/MqttTopicFilterBuilder.cs b/Source/MQTTnet/MqttTopicFilterBuilder.cs index bcf6efd2..9b4b7abd 100644 --- a/Source/MQTTnet/MqttTopicFilterBuilder.cs +++ b/Source/MQTTnet/MqttTopicFilterBuilder.cs @@ -28,62 +28,36 @@ namespace MQTTnet /// string _topic; - /// - /// Adds a topic to the topic filter. - /// - /// The topic. - /// A new instance of the class. public MqttTopicFilterBuilder WithTopic(string topic) { _topic = topic; return this; } - /// - /// Adds the quality of service level to the topic filter. - /// - /// The quality of service level. - /// A new instance of the class. public MqttTopicFilterBuilder WithQualityOfServiceLevel(MqttQualityOfServiceLevel qualityOfServiceLevel) { _qualityOfServiceLevel = qualityOfServiceLevel; return this; } - /// - /// Adds the quality of service level 0 (at least once) to the topic filter. - /// - /// A new instance of the class. public MqttTopicFilterBuilder WithAtLeastOnceQoS() { _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce; return this; } - /// - /// Adds the quality of service level 1 (at most once) to the topic filter. - /// - /// A new instance of the class. public MqttTopicFilterBuilder WithAtMostOnceQoS() { _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce; return this; } - /// - /// Adds the quality of service level 2 (exactly once) to the topic filter. - /// - /// A new instance of the class. public MqttTopicFilterBuilder WithExactlyOnceQoS() { _qualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce; return this; } - /// - /// Builds the topic filter. - /// - /// A new instance of the class. public MqttTopicFilter Build() { if (string.IsNullOrEmpty(_topic)) diff --git a/Source/MQTTnet/Server/CheckSubscriptionsResult.cs b/Source/MQTTnet/Server/CheckSubscriptionsResult.cs index b6e7dbd8..eb66f17e 100644 --- a/Source/MQTTnet/Server/CheckSubscriptionsResult.cs +++ b/Source/MQTTnet/Server/CheckSubscriptionsResult.cs @@ -8,6 +8,14 @@ namespace MQTTnet.Server public bool IsSubscribed { get; set; } + /// + /// Gets or sets the quality of service level. + /// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message. + /// There are 3 QoS levels in MQTT: + /// - At most once (0): Message gets delivered no time, once or multiple times. + /// - At least once (1): Message gets delivered at least once (one time or more often). + /// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once). + /// public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; } } } diff --git a/Source/MQTTnet/Server/IMqttClientSession.cs b/Source/MQTTnet/Server/IMqttClientSession.cs index d7db2b13..f522df68 100644 --- a/Source/MQTTnet/Server/IMqttClientSession.cs +++ b/Source/MQTTnet/Server/IMqttClientSession.cs @@ -4,6 +4,10 @@ namespace MQTTnet.Server { public interface IMqttClientSession { + /// + /// Gets the client identifier. + /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. + /// string ClientId { get; } Task StopAsync(); diff --git a/Source/MQTTnet/Server/IMqttServerFactory.cs b/Source/MQTTnet/Server/IMqttServerFactory.cs index 05090bb7..39efaa3d 100644 --- a/Source/MQTTnet/Server/IMqttServerFactory.cs +++ b/Source/MQTTnet/Server/IMqttServerFactory.cs @@ -7,37 +7,14 @@ namespace MQTTnet.Server { public interface IMqttServerFactory { - /// - /// Gets the default server adapters. - /// IList> DefaultServerAdapters { get; } - /// - /// Creates a new default MQTT server. - /// - /// A new . IMqttServer CreateMqttServer(); - /// - /// Creates a new default MQTT server. - /// - /// The logger. - /// A new . IMqttServer CreateMqttServer(IMqttNetLogger logger); - /// - /// Creates a new default MQTT server. - /// - /// The adapters. - /// A new . IMqttServer CreateMqttServer(IEnumerable adapters); - /// - /// Creates a new default MQTT server. - /// - /// The adapters. - /// The logger. - /// A new . IMqttServer CreateMqttServer(IEnumerable adapters, IMqttNetLogger logger); } } \ No newline at end of file diff --git a/Source/MQTTnet/Server/IMqttServerOptions.cs b/Source/MQTTnet/Server/IMqttServerOptions.cs index 233048e5..cd0cd40b 100644 --- a/Source/MQTTnet/Server/IMqttServerOptions.cs +++ b/Source/MQTTnet/Server/IMqttServerOptions.cs @@ -4,6 +4,10 @@ namespace MQTTnet.Server { public interface IMqttServerOptions { + /// + /// Gets the client identifier. + /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. + /// string ClientId { get; set; } bool EnablePersistentSessions { get; } diff --git a/Source/MQTTnet/Server/IMqttServerPersistedSession.cs b/Source/MQTTnet/Server/IMqttServerPersistedSession.cs index 9ca40761..a7b015ef 100644 --- a/Source/MQTTnet/Server/IMqttServerPersistedSession.cs +++ b/Source/MQTTnet/Server/IMqttServerPersistedSession.cs @@ -5,14 +5,26 @@ namespace MQTTnet.Server { public interface IMqttServerPersistedSession { + /// + /// Gets the client identifier. + /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. + /// string ClientId { get; } IDictionary Items { get; } IList Subscriptions { get; } + /// + /// Gets the last will message. + /// In MQTT, you use the last will message feature to notify other clients about an ungracefully disconnected client. + /// MqttApplicationMessage WillMessage { get; } + /// + /// Gets the will delay interval. + /// This is the time between the client disconnect and the time the will message will be sent. + /// uint? WillDelayInterval { get; } DateTime? SessionExpiryTimestamp { get; } diff --git a/Source/MQTTnet/Server/MqttApplicationMessageInterceptorContext.cs b/Source/MQTTnet/Server/MqttApplicationMessageInterceptorContext.cs index cde9a855..f606a575 100644 --- a/Source/MQTTnet/Server/MqttApplicationMessageInterceptorContext.cs +++ b/Source/MQTTnet/Server/MqttApplicationMessageInterceptorContext.cs @@ -18,6 +18,10 @@ namespace MQTTnet.Server /// public IMqttNetScopedLogger Logger { get; } + /// + /// Gets the client identifier. + /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. + /// public string ClientId { get; } public MqttApplicationMessage ApplicationMessage { get; set; } diff --git a/Source/MQTTnet/Server/MqttClientMessageQueueInterceptorContext.cs b/Source/MQTTnet/Server/MqttClientMessageQueueInterceptorContext.cs index 209f8c03..881666eb 100644 --- a/Source/MQTTnet/Server/MqttClientMessageQueueInterceptorContext.cs +++ b/Source/MQTTnet/Server/MqttClientMessageQueueInterceptorContext.cs @@ -20,6 +20,14 @@ namespace MQTTnet.Server public bool AcceptEnqueue { get; set; } = true; + /// + /// Gets or sets the supscription quality of service level. + /// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message. + /// There are 3 QoS levels in MQTT: + /// - At most once (0): Message gets delivered no time, once or multiple times. + /// - At least once (1): Message gets delivered at least once (one time or more often). + /// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once). + /// public MqttQualityOfServiceLevel SubscriptionQualityOfServiceLevel { get; set; } } } diff --git a/Source/MQTTnet/Server/MqttConnectionValidatorContext.cs b/Source/MQTTnet/Server/MqttConnectionValidatorContext.cs index 9a5b8b95..e9d6aebc 100644 --- a/Source/MQTTnet/Server/MqttConnectionValidatorContext.cs +++ b/Source/MQTTnet/Server/MqttConnectionValidatorContext.cs @@ -21,6 +21,10 @@ namespace MQTTnet.Server SessionItems = sessionItems; } + /// + /// Gets the client identifier. + /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. + /// public string ClientId => _connectPacket.ClientId; public string Endpoint => _clientAdapter.Endpoint; @@ -37,30 +41,88 @@ namespace MQTTnet.Server public string Password => Encoding.UTF8.GetString(RawPassword ?? new byte[0]); + /// + /// Gets or sets the will delay interval. + /// This is the time between the client disconnect and the time the will message will be sent. + /// public MqttApplicationMessage WillMessage => _connectPacket?.WillMessage; + /// + /// Gets or sets a value indicating whether clean sessions are used or not. + /// When a client connects to a broker it can connect using either a non persistent connection (clean session) or a persistent connection. + /// With a non persistent connection the broker doesn't store any subscription information or undelivered messages for the client. + /// This mode is ideal when the client only publishes messages. + /// It can also connect as a durable client using a persistent connection. + /// In this mode, the broker will store subscription information, and undelivered messages for the client. + /// public bool? CleanSession => _connectPacket?.CleanSession; + /// + /// Gets or sets the keep alive period. + /// The connection is normally left open by the client so that is can send and receive data at any time. + /// If no data flows over an open connection for a certain time period then the client will generate a PINGREQ and expect to receive a PINGRESP from the broker. + /// This message exchange confirms that the connection is open and working. + /// This period is known as the keep alive period. + /// public ushort? KeepAlivePeriod => _connectPacket?.KeepAlivePeriod; + /// + /// Gets or sets the user properties. + /// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet. + /// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber. + /// The feature is very similar to the HTTP header concept. + /// Hint: MQTT 5 feature only. + /// public List UserProperties => _connectPacket?.Properties?.UserProperties; + /// + /// Gets or sets the authentication data. + /// Hint: MQTT 5 feature only. + /// public byte[] AuthenticationData => _connectPacket?.Properties?.AuthenticationData; + /// + /// Gets or sets the authentication method. + /// Hint: MQTT 5 feature only. + /// public string AuthenticationMethod => _connectPacket?.Properties?.AuthenticationMethod; public uint? MaximumPacketSize => _connectPacket?.Properties?.MaximumPacketSize; + /// + /// Gets or sets the receive maximum. + /// This gives the maximum length of the receive messages. + /// public ushort? ReceiveMaximum => _connectPacket?.Properties?.ReceiveMaximum; + /// + /// Gets or sets the topic alias maximum. + /// This gives the maximum length of the topic alias. + /// public ushort? TopicAliasMaximum => _connectPacket?.Properties?.TopicAliasMaximum; + /// + /// Gets the request problem information. + /// Hint: MQTT 5 feature only. + /// public bool? RequestProblemInformation => _connectPacket?.Properties?.RequestProblemInformation; + /// + /// Gets the request response information. + /// Hint: MQTT 5 feature only. + /// public bool? RequestResponseInformation => _connectPacket?.Properties?.RequestResponseInformation; + /// + /// Gets the session expiry interval. + /// The time after a session expires when it's not actively used. + /// public uint? SessionExpiryInterval => _connectPacket?.Properties?.SessionExpiryInterval; + /// + /// Gets or sets the will delay interval. + /// This is the time between the client disconnect and the time the will message will be sent. + /// public uint? WillDelayInterval => _connectPacket?.Properties?.WillDelayInterval; /// @@ -79,14 +141,26 @@ namespace MQTTnet.Server } /// - /// This is used for MQTTv5 only. When a MQTTv3 client connects the enum value must be one which is + /// Gets or sets the reason code. When a MQTTv3 client connects the enum value must be one which is /// also supported in MQTTv3. Otherwise the connection attempt will fail because not all codes can be /// converted properly. + /// Hint: MQTT 5 feature only. /// public MqttConnectReasonCode ReasonCode { get; set; } = MqttConnectReasonCode.Success; + /// + /// Gets or sets the response user properties. + /// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet. + /// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber. + /// The feature is very similar to the HTTP header concept. + /// Hint: MQTT 5 feature only. + /// public List ResponseUserProperties { get; set; } + /// + /// Gets or sets the response authentication data. + /// Hint: MQTT 5 feature only. + /// public byte[] ResponseAuthenticationData { get; set; } public string AssignedClientIdentifier { get; set; } diff --git a/Source/MQTTnet/Server/MqttQueuedApplicationMessage.cs b/Source/MQTTnet/Server/MqttQueuedApplicationMessage.cs index 635894b3..5ca163dc 100644 --- a/Source/MQTTnet/Server/MqttQueuedApplicationMessage.cs +++ b/Source/MQTTnet/Server/MqttQueuedApplicationMessage.cs @@ -11,6 +11,14 @@ namespace MQTTnet.Server public bool IsRetainedMessage { get; set; } + /// + /// Gets or sets the subscription quality of service level. + /// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message. + /// There are 3 QoS levels in MQTT: + /// - At most once (0): Message gets delivered no time, once or multiple times. + /// - At least once (1): Message gets delivered at least once (one time or more often). + /// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once). + /// public MqttQualityOfServiceLevel SubscriptionQualityOfServiceLevel { get; set; } [Obsolete("Use 'SubscriptionQualityOfServiceLevel' instead.")] diff --git a/Source/MQTTnet/Server/MqttServerClientConnectedEventArgs.cs b/Source/MQTTnet/Server/MqttServerClientConnectedEventArgs.cs index 71c42c3e..88f0c558 100644 --- a/Source/MQTTnet/Server/MqttServerClientConnectedEventArgs.cs +++ b/Source/MQTTnet/Server/MqttServerClientConnectedEventArgs.cs @@ -9,6 +9,10 @@ namespace MQTTnet.Server ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId)); } + /// + /// Gets the client identifier. + /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. + /// public string ClientId { get; } } } diff --git a/Source/MQTTnet/Server/MqttServerClientDisconnectedEventArgs.cs b/Source/MQTTnet/Server/MqttServerClientDisconnectedEventArgs.cs index 9e742749..d2e7fe55 100644 --- a/Source/MQTTnet/Server/MqttServerClientDisconnectedEventArgs.cs +++ b/Source/MQTTnet/Server/MqttServerClientDisconnectedEventArgs.cs @@ -9,7 +9,11 @@ namespace MQTTnet.Server ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId)); DisconnectType = disconnectType; } - + + /// + /// Gets the client identifier. + /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. + /// public string ClientId { get; } public MqttClientDisconnectType DisconnectType { get; } diff --git a/Source/MQTTnet/Server/MqttServerClientSubscribedTopicEventArgs.cs b/Source/MQTTnet/Server/MqttServerClientSubscribedTopicEventArgs.cs index 94d6e264..3586b731 100644 --- a/Source/MQTTnet/Server/MqttServerClientSubscribedTopicEventArgs.cs +++ b/Source/MQTTnet/Server/MqttServerClientSubscribedTopicEventArgs.cs @@ -10,8 +10,16 @@ namespace MQTTnet.Server TopicFilter = topicFilter ?? throw new ArgumentNullException(nameof(topicFilter)); } + /// + /// Gets the client identifier. + /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. + /// public string ClientId { get; } + /// + /// Gets the topic filter. + /// The topic filter can contain topics and wildcards. + /// public MqttTopicFilter TopicFilter { get; } } } diff --git a/Source/MQTTnet/Server/MqttServerClientUnsubscribedTopicEventArgs.cs b/Source/MQTTnet/Server/MqttServerClientUnsubscribedTopicEventArgs.cs index bb78bb28..8ed1264f 100644 --- a/Source/MQTTnet/Server/MqttServerClientUnsubscribedTopicEventArgs.cs +++ b/Source/MQTTnet/Server/MqttServerClientUnsubscribedTopicEventArgs.cs @@ -10,8 +10,16 @@ namespace MQTTnet.Server TopicFilter = topicFilter ?? throw new ArgumentNullException(nameof(topicFilter)); } + /// + /// Gets the client identifier. + /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. + /// public string ClientId { get; } + /// + /// Gets or sets the topic filter. + /// The topic filter can contain topics and wildcards. + /// public string TopicFilter { get; } } } diff --git a/Source/MQTTnet/Server/MqttServerOptions.cs b/Source/MQTTnet/Server/MqttServerOptions.cs index 31f56e5d..ab52813d 100644 --- a/Source/MQTTnet/Server/MqttServerOptions.cs +++ b/Source/MQTTnet/Server/MqttServerOptions.cs @@ -8,6 +8,10 @@ namespace MQTTnet.Server public MqttServerTlsTcpEndpointOptions TlsEndpointOptions { get; } = new MqttServerTlsTcpEndpointOptions(); + /// + /// Gets or sets the client identifier. + /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. + /// public string ClientId { get; set; } public bool EnablePersistentSessions { get; set; } diff --git a/Source/MQTTnet/Server/MqttSubscriptionInterceptorContext.cs b/Source/MQTTnet/Server/MqttSubscriptionInterceptorContext.cs index 0d800c85..cc186e53 100644 --- a/Source/MQTTnet/Server/MqttSubscriptionInterceptorContext.cs +++ b/Source/MQTTnet/Server/MqttSubscriptionInterceptorContext.cs @@ -11,8 +11,16 @@ namespace MQTTnet.Server SessionItems = sessionItems; } + /// + /// Gets the client identifier. + /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. + /// public string ClientId { get; } + /// + /// Gets or sets the topic filter. + /// The topic filter can contain topics and wildcards. + /// public MqttTopicFilter TopicFilter { get; set; } /// diff --git a/Source/MQTTnet/Server/MqttUnsubscriptionInterceptorContext.cs b/Source/MQTTnet/Server/MqttUnsubscriptionInterceptorContext.cs index b33cbac6..e5f8d755 100644 --- a/Source/MQTTnet/Server/MqttUnsubscriptionInterceptorContext.cs +++ b/Source/MQTTnet/Server/MqttUnsubscriptionInterceptorContext.cs @@ -11,8 +11,17 @@ namespace MQTTnet.Server SessionItems = sessionItems; } + /// + /// Gets the client identifier. + /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. + /// public string ClientId { get; } + /// + /// Gets or sets the MQTT topic. + /// In MQTT, the word topic refers to an UTF-8 string that the broker uses to filter messages for each connected client. + /// The topic consists of one or more topic levels. Each topic level is separated by a forward slash (topic level separator). + /// public string Topic { get; set; } /// diff --git a/Source/MQTTnet/Server/Status/IMqttClientStatus.cs b/Source/MQTTnet/Server/Status/IMqttClientStatus.cs index d7debdc6..c080b510 100644 --- a/Source/MQTTnet/Server/Status/IMqttClientStatus.cs +++ b/Source/MQTTnet/Server/Status/IMqttClientStatus.cs @@ -6,6 +6,10 @@ namespace MQTTnet.Server.Status { public interface IMqttClientStatus { + /// + /// Gets the client identifier. + /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. + /// string ClientId { get; } string Endpoint { get; } diff --git a/Source/MQTTnet/Server/Status/IMqttSessionStatus.cs b/Source/MQTTnet/Server/Status/IMqttSessionStatus.cs index 2ff996b1..115da130 100644 --- a/Source/MQTTnet/Server/Status/IMqttSessionStatus.cs +++ b/Source/MQTTnet/Server/Status/IMqttSessionStatus.cs @@ -5,6 +5,10 @@ namespace MQTTnet.Server.Status { public interface IMqttSessionStatus { + /// + /// Gets the client identifier. + /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. + /// string ClientId { get; } long PendingApplicationMessagesCount { get; } diff --git a/Source/MQTTnet/Server/Status/MqttClientStatus.cs b/Source/MQTTnet/Server/Status/MqttClientStatus.cs index 9acfea3e..eca5d1a5 100644 --- a/Source/MQTTnet/Server/Status/MqttClientStatus.cs +++ b/Source/MQTTnet/Server/Status/MqttClientStatus.cs @@ -14,6 +14,10 @@ namespace MQTTnet.Server.Status _connection = connection ?? throw new ArgumentNullException(nameof(connection)); } + /// + /// Gets or sets the client identifier. + /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. + /// public string ClientId { get; set; } public string Endpoint { get; set; } diff --git a/Source/MQTTnet/Server/Status/MqttSessionStatus.cs b/Source/MQTTnet/Server/Status/MqttSessionStatus.cs index a523a37e..af5a6e88 100644 --- a/Source/MQTTnet/Server/Status/MqttSessionStatus.cs +++ b/Source/MQTTnet/Server/Status/MqttSessionStatus.cs @@ -15,6 +15,10 @@ namespace MQTTnet.Server.Status _sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager)); } + /// + /// Gets or sets the client identifier. + /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. + /// public string ClientId { get; set; } public long PendingApplicationMessagesCount { get; set; } From d804b805c14d85069fb2f6f2b83171def41d7352 Mon Sep 17 00:00:00 2001 From: SeppPenner Date: Fri, 15 Jan 2021 20:41:47 +0100 Subject: [PATCH 04/29] Smaller changes on the comments. --- .../IManagedMqttClient.cs | 8 ++++---- Source/MQTTnet/Client/IMqttClient.cs | 4 ---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs b/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs index 720ed127..9a76f092 100644 --- a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs +++ b/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs @@ -19,11 +19,11 @@ namespace MQTTnet.Extensions.ManagedClient bool IsStarted { get; } bool IsConnected { get; } - + int PendingApplicationMessagesCount { get; } - + IManagedMqttClientOptions Options { get; } - + IMqttClientConnectedHandler ConnectedHandler { get; set; } IMqttClientDisconnectedHandler DisconnectedHandler { get; set; } @@ -33,7 +33,7 @@ namespace MQTTnet.Extensions.ManagedClient IApplicationMessageSkippedHandler ApplicationMessageSkippedHandler { get; set; } IConnectingFailedHandler ConnectingFailedHandler { get; set; } - + ISynchronizingSubscriptionsFailedHandler SynchronizingSubscriptionsFailedHandler { get; set; } Task StartAsync(IManagedMqttClientOptions options); diff --git a/Source/MQTTnet/Client/IMqttClient.cs b/Source/MQTTnet/Client/IMqttClient.cs index dbe005a0..394ec8dc 100644 --- a/Source/MQTTnet/Client/IMqttClient.cs +++ b/Source/MQTTnet/Client/IMqttClient.cs @@ -14,10 +14,6 @@ namespace MQTTnet.Client { bool IsConnected { get; } - /// - /// Gets the options. - /// This contains all the set options for the client. - /// IMqttClientOptions Options { get; } /// From 3d8d06f9b3e15e872a5641058866844954001349 Mon Sep 17 00:00:00 2001 From: SeppPenner Date: Fri, 15 Jan 2021 20:43:57 +0100 Subject: [PATCH 05/29] Hopefully completely reset the IManagedMqttClient file. --- .../IManagedMqttClient.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs b/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs index 9a76f092..cc2490cb 100644 --- a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs +++ b/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs @@ -17,33 +17,33 @@ namespace MQTTnet.Extensions.ManagedClient IMqttClient InternalClient { get; } bool IsStarted { get; } - + bool IsConnected { get; } - + int PendingApplicationMessagesCount { get; } - + IManagedMqttClientOptions Options { get; } - + IMqttClientConnectedHandler ConnectedHandler { get; set; } - + IMqttClientDisconnectedHandler DisconnectedHandler { get; set; } IApplicationMessageProcessedHandler ApplicationMessageProcessedHandler { get; set; } - + IApplicationMessageSkippedHandler ApplicationMessageSkippedHandler { get; set; } IConnectingFailedHandler ConnectingFailedHandler { get; set; } - + ISynchronizingSubscriptionsFailedHandler SynchronizingSubscriptionsFailedHandler { get; set; } Task StartAsync(IManagedMqttClientOptions options); - + Task StopAsync(); - + Task PingAsync(CancellationToken cancellationToken); Task SubscribeAsync(IEnumerable topicFilters); - + Task UnsubscribeAsync(IEnumerable topics); Task PublishAsync(ManagedMqttApplicationMessage applicationMessages); From c1001db4507f223ab91336e7c4d39f365925351e Mon Sep 17 00:00:00 2001 From: Christian Date: Sun, 24 Jan 2021 20:58:15 +0100 Subject: [PATCH 06/29] Add packet inspector API. --- .../MqttWebSocketServerAdapter.cs | 2 +- .../WebSocket4NetMqttClientAdapterFactory.cs | 22 ++- Source/MQTTnet.Server/MQTTnet.Server.csproj | 2 +- Source/MQTTnet/Adapter/MqttChannelAdapter.cs | 134 ++++++++++++-- .../Adapter/MqttPacketInspectorHandler.cs | 84 +++++++++ Source/MQTTnet/Adapter/ReceivedMqttPacket.cs | 9 +- .../Client/Options/IMqttClientOptions.cs | 3 + .../Client/Options/MqttClientOptions.cs | 3 + .../Options/MqttClientOptionsBuilder.cs | 7 + .../PacketInspection/IMqttPacketInspector.cs | 7 + .../MqttPacketFlowDirection.cs | 9 + .../ProcessMqttPacketContext.cs | 9 + .../Formatter/MqttPacketFormatterAdapter.cs | 18 +- Source/MQTTnet/Formatter/MqttPacketReader.cs | 114 ------------ .../Formatter/V3/MqttV310PacketFormatter.cs | 30 +-- .../Formatter/V5/MqttV500PacketDecoder.cs | 26 +-- .../MqttClientAdapterFactory.cs | 11 +- .../MQTTnet/Implementations/MqttTcpChannel.cs | 4 +- .../MqttTcpServerAdapter.Uwp.cs | 2 +- .../Implementations/MqttTcpServerListener.cs | 6 +- Source/MQTTnet/Internal/TestMqttChannel.cs | 7 +- Source/MQTTnet/MqttTopicFilterBuilder.cs | 30 ++- .../ChannelAdapterBenchmark.cs | 2 +- .../MQTTnet.Benchmarks/SerializerBenchmark.cs | 11 +- .../MqttPacketReader_Tests.cs | 43 ++--- .../MqttPacketSerializer_Tests.cs | 174 ++++++++++-------- 26 files changed, 480 insertions(+), 289 deletions(-) create mode 100644 Source/MQTTnet/Adapter/MqttPacketInspectorHandler.cs create mode 100644 Source/MQTTnet/Diagnostics/PacketInspection/IMqttPacketInspector.cs create mode 100644 Source/MQTTnet/Diagnostics/PacketInspection/MqttPacketFlowDirection.cs create mode 100644 Source/MQTTnet/Diagnostics/PacketInspection/ProcessMqttPacketContext.cs delete mode 100644 Source/MQTTnet/Formatter/MqttPacketReader.cs diff --git a/Source/MQTTnet.AspnetCore/MqttWebSocketServerAdapter.cs b/Source/MQTTnet.AspnetCore/MqttWebSocketServerAdapter.cs index 5520b936..cec833eb 100644 --- a/Source/MQTTnet.AspnetCore/MqttWebSocketServerAdapter.cs +++ b/Source/MQTTnet.AspnetCore/MqttWebSocketServerAdapter.cs @@ -49,7 +49,7 @@ namespace MQTTnet.AspNetCore var formatter = new MqttPacketFormatterAdapter(writer); var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection, clientCertificate); - using (var channelAdapter = new MqttChannelAdapter(channel, formatter, _rootLogger)) + using (var channelAdapter = new MqttChannelAdapter(channel, formatter, null, _rootLogger)) { await clientHandler(channelAdapter).ConfigureAwait(false); } diff --git a/Source/MQTTnet.Extensions.WebSocket4Net/WebSocket4NetMqttClientAdapterFactory.cs b/Source/MQTTnet.Extensions.WebSocket4Net/WebSocket4NetMqttClientAdapterFactory.cs index 96f3c305..63826532 100644 --- a/Source/MQTTnet.Extensions.WebSocket4Net/WebSocket4NetMqttClientAdapterFactory.cs +++ b/Source/MQTTnet.Extensions.WebSocket4Net/WebSocket4NetMqttClientAdapterFactory.cs @@ -7,7 +7,7 @@ using System; namespace MQTTnet.Extensions.WebSocket4Net { - public class WebSocket4NetMqttClientAdapterFactory : IMqttClientAdapterFactory + public sealed class WebSocket4NetMqttClientAdapterFactory : IMqttClientAdapterFactory { readonly IMqttNetLogger _logger; @@ -23,14 +23,22 @@ namespace MQTTnet.Extensions.WebSocket4Net switch (options.ChannelOptions) { case MqttClientTcpOptions _: - { - return new MqttChannelAdapter(new MqttTcpChannel(options), new MqttPacketFormatterAdapter(options.ProtocolVersion), _logger); - } + { + return new MqttChannelAdapter( + new MqttTcpChannel(options), + new MqttPacketFormatterAdapter(options.ProtocolVersion), + options.PacketInspector, + _logger); + } case MqttClientWebSocketOptions webSocketOptions: - { - return new MqttChannelAdapter(new WebSocket4NetMqttChannel(options, webSocketOptions), new MqttPacketFormatterAdapter(options.ProtocolVersion), _logger); - } + { + return new MqttChannelAdapter( + new WebSocket4NetMqttChannel(options, webSocketOptions), + new MqttPacketFormatterAdapter(options.ProtocolVersion), + options.PacketInspector, + _logger); + } default: { diff --git a/Source/MQTTnet.Server/MQTTnet.Server.csproj b/Source/MQTTnet.Server/MQTTnet.Server.csproj index 337a0636..3ea975cb 100644 --- a/Source/MQTTnet.Server/MQTTnet.Server.csproj +++ b/Source/MQTTnet.Server/MQTTnet.Server.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1;net5.0 + net5.0 InProcess MQTTnet.Server MQTTnet.Server diff --git a/Source/MQTTnet/Adapter/MqttChannelAdapter.cs b/Source/MQTTnet/Adapter/MqttChannelAdapter.cs index 1b6bc068..8e3e8ddf 100644 --- a/Source/MQTTnet/Adapter/MqttChannelAdapter.cs +++ b/Source/MQTTnet/Adapter/MqttChannelAdapter.cs @@ -11,6 +11,7 @@ using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; +using MQTTnet.Diagnostics.PacketInspection; namespace MQTTnet.Adapter { @@ -19,26 +20,26 @@ namespace MQTTnet.Adapter const uint ErrorOperationAborted = 0x800703E3; const int ReadBufferSize = 4096; + readonly byte[] _singleByteBuffer = new byte[1]; + readonly byte[] _fixedHeaderBuffer = new byte[2]; + + readonly MqttPacketInspectorHandler _packetInspectorHandler; readonly IMqttNetScopedLogger _logger; readonly IMqttChannel _channel; - readonly MqttPacketReader _packetReader; - - readonly byte[] _fixedHeaderBuffer = new byte[2]; readonly AsyncLock _syncRoot = new AsyncLock(); long _bytesReceived; long _bytesSent; - public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, IMqttNetLogger logger) + public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, IMqttPacketInspector packetInspector, IMqttNetLogger logger) { - if (logger == null) throw new ArgumentNullException(nameof(logger)); - _channel = channel ?? throw new ArgumentNullException(nameof(channel)); PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter)); - _packetReader = new MqttPacketReader(_channel); + _packetInspectorHandler = new MqttPacketInspectorHandler(packetInspector, logger); + if (logger == null) throw new ArgumentNullException(nameof(logger)); _logger = logger.CreateScopedLogger(nameof(MqttChannelAdapter)); } @@ -124,6 +125,7 @@ namespace MQTTnet.Adapter try { var packetData = PacketFormatterAdapter.Encode(packet); + _packetInspectorHandler.BeginSendPacket(packetData); await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false); @@ -154,12 +156,16 @@ namespace MQTTnet.Adapter try { + _packetInspectorHandler.BeginReceivePacket(); + var receivedPacket = await ReceiveAsync(cancellationToken).ConfigureAwait(false); if (receivedPacket == null || cancellationToken.IsCancellationRequested) { return null; } + _packetInspectorHandler.EndReceivePacket(); + Interlocked.Add(ref _bytesSent, receivedPacket.TotalLength); if (PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.Unknown) @@ -215,7 +221,12 @@ namespace MQTTnet.Adapter async Task ReceiveAsync(CancellationToken cancellationToken) { - var readFixedHeaderResult = await _packetReader.ReadFixedHeaderAsync(_fixedHeaderBuffer, cancellationToken).ConfigureAwait(false); + if (cancellationToken.IsCancellationRequested) + { + return null; + } + + var readFixedHeaderResult = await ReadFixedHeaderAsync(cancellationToken).ConfigureAwait(false); if (cancellationToken.IsCancellationRequested) { @@ -234,7 +245,7 @@ namespace MQTTnet.Adapter var fixedHeader = readFixedHeaderResult.FixedHeader; if (fixedHeader.RemainingLength == 0) { - return new ReceivedMqttPacket(fixedHeader.Flags, null, 2); + return new ReceivedMqttPacket(fixedHeader.Flags, new MqttPacketBodyReader(new byte[0], 0, 0), 2); } var bodyLength = fixedHeader.RemainingLength; @@ -266,6 +277,8 @@ namespace MQTTnet.Adapter bodyOffset += readBytes; } while (bodyOffset < bodyLength); + _packetInspectorHandler.FillReceiveBuffer(body); + var bodyReader = new MqttPacketBodyReader(body, 0, bodyLength); return new ReceivedMqttPacket(fixedHeader.Flags, bodyReader, fixedHeader.TotalLength); } @@ -275,11 +288,110 @@ namespace MQTTnet.Adapter } } + async Task ReadFixedHeaderAsync(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. + var buffer = _fixedHeaderBuffer; + var totalBytesRead = 0; + + while (totalBytesRead < buffer.Length) + { + var bytesRead = await _channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false); + + if (cancellationToken.IsCancellationRequested) + { + return null; + } + + if (bytesRead == 0) + { + return new ReadFixedHeaderResult + { + ConnectionClosed = true + }; + } + + totalBytesRead += bytesRead; + } + + _packetInspectorHandler.FillReceiveBuffer(buffer); + + var hasRemainingLength = buffer[1] != 0; + if (!hasRemainingLength) + { + return new ReadFixedHeaderResult + { + FixedHeader = new MqttFixedHeader(buffer[0], 0, totalBytesRead) + }; + } + + var bodyLength = await ReadBodyLengthAsync(buffer[1], cancellationToken).ConfigureAwait(false); + + if (!bodyLength.HasValue) + { + return new ReadFixedHeaderResult + { + ConnectionClosed = true + }; + } + + totalBytesRead += bodyLength.Value; + return new ReadFixedHeaderResult + { + FixedHeader = new MqttFixedHeader(buffer[0], bodyLength.Value, totalBytesRead) + }; + } + + async Task ReadBodyLengthAsync(byte initialEncodedByte, 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."); + } + + if (cancellationToken.IsCancellationRequested) + { + return null; + } + + var readCount = await _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false); + + if (cancellationToken.IsCancellationRequested) + { + return null; + } + + if (readCount == 0) + { + return null; + } + + _packetInspectorHandler.FillReceiveBuffer(_singleByteBuffer); + + encodedByte = _singleByteBuffer[0]; + + value += (encodedByte & 127) * multiplier; + multiplier *= 128; + } + + return value; + } + static bool IsWrappedException(Exception exception) { return exception is OperationCanceledException || exception is MqttCommunicationTimedOutException || - exception is MqttCommunicationException; + exception is MqttCommunicationException || + exception is MqttProtocolViolationException; } static void WrapAndThrowException(Exception exception) @@ -295,7 +407,7 @@ namespace MQTTnet.Adapter { throw new OperationCanceledException(); } - + if (socketException.SocketErrorCode == SocketError.ConnectionAborted) { throw new MqttCommunicationException(socketException); diff --git a/Source/MQTTnet/Adapter/MqttPacketInspectorHandler.cs b/Source/MQTTnet/Adapter/MqttPacketInspectorHandler.cs new file mode 100644 index 00000000..f8048d91 --- /dev/null +++ b/Source/MQTTnet/Adapter/MqttPacketInspectorHandler.cs @@ -0,0 +1,84 @@ +using System; +using System.IO; +using System.Linq; +using MQTTnet.Diagnostics; +using MQTTnet.Diagnostics.PacketInspection; + +namespace MQTTnet.Adapter +{ + public sealed class MqttPacketInspectorHandler + { + readonly MemoryStream _receivedPacketBuffer; + readonly IMqttPacketInspector _packetInspector; + readonly IMqttNetScopedLogger _logger; + + public MqttPacketInspectorHandler(IMqttPacketInspector packetInspector, IMqttNetLogger logger) + { + _packetInspector = packetInspector; + + if (packetInspector != null) + { + _receivedPacketBuffer = new MemoryStream(); + } + + if (logger == null) throw new ArgumentNullException(nameof(logger)); + _logger = logger.CreateScopedLogger(nameof(MqttPacketInspectorHandler)); + } + + public void BeginReceivePacket() + { + _receivedPacketBuffer?.SetLength(0); + } + + public void EndReceivePacket() + { + if (_packetInspector == null) + { + return; + } + + var buffer = _receivedPacketBuffer.ToArray(); + _receivedPacketBuffer.SetLength(0); + + InspectPacket(buffer, MqttPacketFlowDirection.Inbound); + } + + public void BeginSendPacket(ArraySegment buffer) + { + if (_packetInspector == null) + { + return; + } + + // Create a copy of the actual packet so that the inspector gets no access + // to the internal buffers. This is waste of memory but this feature is only + // intended for debugging etc. so that this is OK. + var bufferCopy = buffer.ToArray(); + + InspectPacket(bufferCopy, MqttPacketFlowDirection.Outbound); + } + + public void FillReceiveBuffer(byte[] buffer) + { + _receivedPacketBuffer?.Write(buffer, 0, buffer.Length); + } + + void InspectPacket(byte[] buffer, MqttPacketFlowDirection direction) + { + try + { + var context = new ProcessMqttPacketContext + { + Buffer = buffer, + Direction = direction + }; + + _packetInspector.ProcessMqttPacket(context); + } + catch (Exception exception) + { + _logger.Error(exception, "Error while inspecting packet."); + } + } + } +} \ No newline at end of file diff --git a/Source/MQTTnet/Adapter/ReceivedMqttPacket.cs b/Source/MQTTnet/Adapter/ReceivedMqttPacket.cs index 783f1d82..4a3c9314 100644 --- a/Source/MQTTnet/Adapter/ReceivedMqttPacket.cs +++ b/Source/MQTTnet/Adapter/ReceivedMqttPacket.cs @@ -1,19 +1,20 @@ -using MQTTnet.Formatter; +using System; +using MQTTnet.Formatter; namespace MQTTnet.Adapter { public sealed class ReceivedMqttPacket { - public ReceivedMqttPacket(byte fixedHeader, IMqttPacketBodyReader body, int totalLength) + public ReceivedMqttPacket(byte fixedHeader, IMqttPacketBodyReader bodyReader, int totalLength) { FixedHeader = fixedHeader; - Body = body; + BodyReader = bodyReader ?? throw new ArgumentNullException(nameof(bodyReader)); TotalLength = totalLength; } public byte FixedHeader { get; } - public IMqttPacketBodyReader Body { get; } + public IMqttPacketBodyReader BodyReader { get; } public int TotalLength { get; } } diff --git a/Source/MQTTnet/Client/Options/IMqttClientOptions.cs b/Source/MQTTnet/Client/Options/IMqttClientOptions.cs index e46162ae..8bf89ee5 100644 --- a/Source/MQTTnet/Client/Options/IMqttClientOptions.cs +++ b/Source/MQTTnet/Client/Options/IMqttClientOptions.cs @@ -3,6 +3,7 @@ using MQTTnet.Formatter; using MQTTnet.Packets; using System; using System.Collections.Generic; +using MQTTnet.Diagnostics.PacketInspection; namespace MQTTnet.Client.Options { @@ -29,5 +30,7 @@ namespace MQTTnet.Client.Options uint? SessionExpiryInterval { get; } ushort? TopicAliasMaximum { get; } List UserProperties { get; set; } + + IMqttPacketInspector PacketInspector { get; set; } } } \ No newline at end of file diff --git a/Source/MQTTnet/Client/Options/MqttClientOptions.cs b/Source/MQTTnet/Client/Options/MqttClientOptions.cs index 79c6c702..ad937165 100644 --- a/Source/MQTTnet/Client/Options/MqttClientOptions.cs +++ b/Source/MQTTnet/Client/Options/MqttClientOptions.cs @@ -3,6 +3,7 @@ using MQTTnet.Formatter; using MQTTnet.Packets; using System; using System.Collections.Generic; +using MQTTnet.Diagnostics.PacketInspection; namespace MQTTnet.Client.Options { @@ -31,5 +32,7 @@ namespace MQTTnet.Client.Options public uint? SessionExpiryInterval { get; set; } public ushort? TopicAliasMaximum { get; set; } public List UserProperties { get; set; } + + public IMqttPacketInspector PacketInspector { get; set; } } } diff --git a/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs b/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs index 68d1aa3f..c12c468e 100644 --- a/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs +++ b/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using MQTTnet.Diagnostics.PacketInspection; namespace MQTTnet.Client.Options { @@ -256,6 +257,12 @@ namespace MQTTnet.Client.Options return this; } + public MqttClientOptionsBuilder WithPacketInspector(IMqttPacketInspector packetInspector) + { + _options.PacketInspector = packetInspector; + return this; + } + public IMqttClientOptions Build() { if (_tcpOptions == null && _webSocketOptions == null) diff --git a/Source/MQTTnet/Diagnostics/PacketInspection/IMqttPacketInspector.cs b/Source/MQTTnet/Diagnostics/PacketInspection/IMqttPacketInspector.cs new file mode 100644 index 00000000..9d7db80c --- /dev/null +++ b/Source/MQTTnet/Diagnostics/PacketInspection/IMqttPacketInspector.cs @@ -0,0 +1,7 @@ +namespace MQTTnet.Diagnostics.PacketInspection +{ + public interface IMqttPacketInspector + { + void ProcessMqttPacket(ProcessMqttPacketContext context); + } +} \ No newline at end of file diff --git a/Source/MQTTnet/Diagnostics/PacketInspection/MqttPacketFlowDirection.cs b/Source/MQTTnet/Diagnostics/PacketInspection/MqttPacketFlowDirection.cs new file mode 100644 index 00000000..7f391179 --- /dev/null +++ b/Source/MQTTnet/Diagnostics/PacketInspection/MqttPacketFlowDirection.cs @@ -0,0 +1,9 @@ +namespace MQTTnet.Diagnostics.PacketInspection +{ + public enum MqttPacketFlowDirection + { + Inbound, + + Outbound + } +} \ No newline at end of file diff --git a/Source/MQTTnet/Diagnostics/PacketInspection/ProcessMqttPacketContext.cs b/Source/MQTTnet/Diagnostics/PacketInspection/ProcessMqttPacketContext.cs new file mode 100644 index 00000000..644b2a77 --- /dev/null +++ b/Source/MQTTnet/Diagnostics/PacketInspection/ProcessMqttPacketContext.cs @@ -0,0 +1,9 @@ +namespace MQTTnet.Diagnostics.PacketInspection +{ + public sealed class ProcessMqttPacketContext + { + public MqttPacketFlowDirection Direction { get; set; } + + public byte[] Buffer { get; set; } + } +} \ No newline at end of file diff --git a/Source/MQTTnet/Formatter/MqttPacketFormatterAdapter.cs b/Source/MQTTnet/Formatter/MqttPacketFormatterAdapter.cs index 7757accd..1e2df944 100644 --- a/Source/MQTTnet/Formatter/MqttPacketFormatterAdapter.cs +++ b/Source/MQTTnet/Formatter/MqttPacketFormatterAdapter.cs @@ -11,7 +11,7 @@ namespace MQTTnet.Formatter public sealed class MqttPacketFormatterAdapter { IMqttPacketFormatter _formatter; - + public MqttPacketFormatterAdapter(MqttProtocolVersion protocolVersion) : this(protocolVersion, new MqttPacketWriter()) { @@ -26,7 +26,7 @@ namespace MQTTnet.Formatter public MqttPacketFormatterAdapter(IMqttPacketWriter writer) { Writer = writer; - } + } public MqttProtocolVersion ProtocolVersion { get; private set; } = MqttProtocolVersion.Unknown; @@ -39,7 +39,7 @@ namespace MQTTnet.Formatter return _formatter.DataConverter; } } - + public IMqttPacketWriter Writer { get; } public ArraySegment Encode(MqttBasePacket packet) @@ -69,10 +69,10 @@ namespace MQTTnet.Formatter { var protocolVersion = ParseProtocolVersion(receivedMqttPacket); - // Reset the position of the stream because the protocol version is part of + // Reset the position of the stream because the protocol version is part of // the regular CONNECT packet. So it will not properly deserialized if this // data is missing. - receivedMqttPacket.Body.Seek(0); + receivedMqttPacket.BodyReader.Seek(0); UseProtocolVersion(protocolVersion); } @@ -83,7 +83,7 @@ namespace MQTTnet.Formatter { throw new InvalidOperationException("MQTT protocol version is invalid."); } - + switch (protocolVersion) { case MqttProtocolVersion.V500: @@ -120,7 +120,7 @@ namespace MQTTnet.Formatter { if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket)); - if (receivedMqttPacket.Body.Length < 7) + if (receivedMqttPacket.BodyReader.Length < 7) { // 2 byte protocol name length // at least 4 byte protocol name @@ -128,8 +128,8 @@ namespace MQTTnet.Formatter throw new MqttProtocolViolationException("CONNECT packet must have at least 7 bytes."); } - var protocolName = receivedMqttPacket.Body.ReadStringWithLengthPrefix(); - var protocolLevel = receivedMqttPacket.Body.ReadByte(); + var protocolName = receivedMqttPacket.BodyReader.ReadStringWithLengthPrefix(); + var protocolLevel = receivedMqttPacket.BodyReader.ReadByte(); if (protocolName == "MQTT") { diff --git a/Source/MQTTnet/Formatter/MqttPacketReader.cs b/Source/MQTTnet/Formatter/MqttPacketReader.cs deleted file mode 100644 index 0f1a9760..00000000 --- a/Source/MQTTnet/Formatter/MqttPacketReader.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using MQTTnet.Channel; -using MQTTnet.Exceptions; - -namespace MQTTnet.Formatter -{ - public sealed class MqttPacketReader - { - readonly byte[] _singleByteBuffer = new byte[1]; - - readonly IMqttChannel _channel; - - public MqttPacketReader(IMqttChannel channel) - { - _channel = channel ?? throw new ArgumentNullException(nameof(channel)); - } - - public async Task ReadFixedHeaderAsync(byte[] fixedHeaderBuffer, 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. - var buffer = fixedHeaderBuffer; - var totalBytesRead = 0; - - while (totalBytesRead < buffer.Length) - { - var bytesRead = await _channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false); - - if (cancellationToken.IsCancellationRequested) - { - return null; - } - - if (bytesRead == 0) - { - return new ReadFixedHeaderResult - { - ConnectionClosed = true - }; - } - - totalBytesRead += bytesRead; - } - - var hasRemainingLength = buffer[1] != 0; - if (!hasRemainingLength) - { - return new ReadFixedHeaderResult - { - FixedHeader = new MqttFixedHeader(buffer[0], 0, totalBytesRead) - }; - } - - var bodyLength = await ReadBodyLengthAsync(buffer[1], cancellationToken).ConfigureAwait(false); - - if (!bodyLength.HasValue) - { - return new ReadFixedHeaderResult - { - ConnectionClosed = true - }; - } - - totalBytesRead += bodyLength.Value; - return new ReadFixedHeaderResult - { - FixedHeader = new MqttFixedHeader(buffer[0], bodyLength.Value, totalBytesRead) - }; - } - - async Task ReadBodyLengthAsync(byte initialEncodedByte, 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."); - } - - if (cancellationToken.IsCancellationRequested) - { - return null; - } - - var readCount = await _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false); - - if (cancellationToken.IsCancellationRequested) - { - return null; - } - - if (readCount == 0) - { - return null; - } - - encodedByte = _singleByteBuffer[0]; - - value += (encodedByte & 127) * multiplier; - multiplier *= 128; - } - - return value; - } - } -} diff --git a/Source/MQTTnet/Formatter/V3/MqttV310PacketFormatter.cs b/Source/MQTTnet/Formatter/V3/MqttV310PacketFormatter.cs index 43ffc1da..d872b1cf 100644 --- a/Source/MQTTnet/Formatter/V3/MqttV310PacketFormatter.cs +++ b/Source/MQTTnet/Formatter/V3/MqttV310PacketFormatter.cs @@ -63,20 +63,20 @@ namespace MQTTnet.Formatter.V3 switch ((MqttControlPacketType)controlPacketType) { - case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.Body); - case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.Body); + case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.BodyReader); + case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.BodyReader); case MqttControlPacketType.Disconnect: return DisconnectPacket; case MqttControlPacketType.Publish: return DecodePublishPacket(receivedMqttPacket); - case MqttControlPacketType.PubAck: return DecodePubAckPacket(receivedMqttPacket.Body); - case MqttControlPacketType.PubRec: return DecodePubRecPacket(receivedMqttPacket.Body); - case MqttControlPacketType.PubRel: return DecodePubRelPacket(receivedMqttPacket.Body); - case MqttControlPacketType.PubComp: return DecodePubCompPacket(receivedMqttPacket.Body); + case MqttControlPacketType.PubAck: return DecodePubAckPacket(receivedMqttPacket.BodyReader); + case MqttControlPacketType.PubRec: return DecodePubRecPacket(receivedMqttPacket.BodyReader); + case MqttControlPacketType.PubRel: return DecodePubRelPacket(receivedMqttPacket.BodyReader); + case MqttControlPacketType.PubComp: return DecodePubCompPacket(receivedMqttPacket.BodyReader); case MqttControlPacketType.PingReq: return PingReqPacket; case MqttControlPacketType.PingResp: return PingRespPacket; - case MqttControlPacketType.Subscribe: return DecodeSubscribePacket(receivedMqttPacket.Body); - case MqttControlPacketType.SubAck: return DecodeSubAckPacket(receivedMqttPacket.Body); - case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribePacket(receivedMqttPacket.Body); - case MqttControlPacketType.UnsubAck: return DecodeUnsubAckPacket(receivedMqttPacket.Body); + case MqttControlPacketType.Subscribe: return DecodeSubscribePacket(receivedMqttPacket.BodyReader); + case MqttControlPacketType.SubAck: return DecodeSubAckPacket(receivedMqttPacket.BodyReader); + case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribePacket(receivedMqttPacket.BodyReader); + case MqttControlPacketType.UnsubAck: return DecodeUnsubAckPacket(receivedMqttPacket.BodyReader); default: throw new MqttProtocolViolationException($"Packet type ({controlPacketType}) not supported."); } @@ -202,18 +202,18 @@ namespace MQTTnet.Formatter.V3 static MqttBasePacket DecodePublishPacket(ReceivedMqttPacket receivedMqttPacket) { - ThrowIfBodyIsEmpty(receivedMqttPacket.Body); + ThrowIfBodyIsEmpty(receivedMqttPacket.BodyReader); var retain = (receivedMqttPacket.FixedHeader & 0x1) > 0; var qualityOfServiceLevel = (MqttQualityOfServiceLevel)(receivedMqttPacket.FixedHeader >> 1 & 0x3); var dup = (receivedMqttPacket.FixedHeader & 0x8) > 0; - var topic = receivedMqttPacket.Body.ReadStringWithLengthPrefix(); + var topic = receivedMqttPacket.BodyReader.ReadStringWithLengthPrefix(); ushort packetIdentifier = 0; if (qualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce) { - packetIdentifier = receivedMqttPacket.Body.ReadTwoByteInteger(); + packetIdentifier = receivedMqttPacket.BodyReader.ReadTwoByteInteger(); } var packet = new MqttPublishPacket @@ -225,9 +225,9 @@ namespace MQTTnet.Formatter.V3 Dup = dup }; - if (!receivedMqttPacket.Body.EndOfStream) + if (!receivedMqttPacket.BodyReader.EndOfStream) { - packet.Payload = receivedMqttPacket.Body.ReadRemainingData(); + packet.Payload = receivedMqttPacket.BodyReader.ReadRemainingData(); } return packet; diff --git a/Source/MQTTnet/Formatter/V5/MqttV500PacketDecoder.cs b/Source/MQTTnet/Formatter/V5/MqttV500PacketDecoder.cs index be8d74f4..fe355060 100644 --- a/Source/MQTTnet/Formatter/V5/MqttV500PacketDecoder.cs +++ b/Source/MQTTnet/Formatter/V5/MqttV500PacketDecoder.cs @@ -25,21 +25,21 @@ namespace MQTTnet.Formatter.V5 switch ((MqttControlPacketType)controlPacketType) { - case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.Body); - case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.Body); - case MqttControlPacketType.Disconnect: return DecodeDisconnectPacket(receivedMqttPacket.Body); - case MqttControlPacketType.Publish: return DecodePublishPacket(receivedMqttPacket.FixedHeader, receivedMqttPacket.Body); - case MqttControlPacketType.PubAck: return DecodePubAckPacket(receivedMqttPacket.Body); - case MqttControlPacketType.PubRec: return DecodePubRecPacket(receivedMqttPacket.Body); - case MqttControlPacketType.PubRel: return DecodePubRelPacket(receivedMqttPacket.Body); - case MqttControlPacketType.PubComp: return DecodePubCompPacket(receivedMqttPacket.Body); + case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.BodyReader); + case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.BodyReader); + case MqttControlPacketType.Disconnect: return DecodeDisconnectPacket(receivedMqttPacket.BodyReader); + case MqttControlPacketType.Publish: return DecodePublishPacket(receivedMqttPacket.FixedHeader, receivedMqttPacket.BodyReader); + case MqttControlPacketType.PubAck: return DecodePubAckPacket(receivedMqttPacket.BodyReader); + case MqttControlPacketType.PubRec: return DecodePubRecPacket(receivedMqttPacket.BodyReader); + case MqttControlPacketType.PubRel: return DecodePubRelPacket(receivedMqttPacket.BodyReader); + case MqttControlPacketType.PubComp: return DecodePubCompPacket(receivedMqttPacket.BodyReader); case MqttControlPacketType.PingReq: return DecodePingReqPacket(); case MqttControlPacketType.PingResp: return DecodePingRespPacket(); - case MqttControlPacketType.Subscribe: return DecodeSubscribePacket(receivedMqttPacket.Body); - case MqttControlPacketType.SubAck: return DecodeSubAckPacket(receivedMqttPacket.Body); - case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribePacket(receivedMqttPacket.Body); - case MqttControlPacketType.UnsubAck: return DecodeUnsubAckPacket(receivedMqttPacket.Body); - case MqttControlPacketType.Auth: return DecodeAuthPacket(receivedMqttPacket.Body); + case MqttControlPacketType.Subscribe: return DecodeSubscribePacket(receivedMqttPacket.BodyReader); + case MqttControlPacketType.SubAck: return DecodeSubAckPacket(receivedMqttPacket.BodyReader); + case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribePacket(receivedMqttPacket.BodyReader); + case MqttControlPacketType.UnsubAck: return DecodeUnsubAckPacket(receivedMqttPacket.BodyReader); + case MqttControlPacketType.Auth: return DecodeAuthPacket(receivedMqttPacket.BodyReader); default: throw new MqttProtocolViolationException($"Packet type ({controlPacketType}) not supported."); } diff --git a/Source/MQTTnet/Implementations/MqttClientAdapterFactory.cs b/Source/MQTTnet/Implementations/MqttClientAdapterFactory.cs index d7e85db4..920f9822 100644 --- a/Source/MQTTnet/Implementations/MqttClientAdapterFactory.cs +++ b/Source/MQTTnet/Implementations/MqttClientAdapterFactory.cs @@ -3,6 +3,7 @@ using MQTTnet.Client.Options; using MQTTnet.Diagnostics; using MQTTnet.Formatter; using System; +using MQTTnet.Channel; namespace MQTTnet.Implementations { @@ -19,16 +20,19 @@ namespace MQTTnet.Implementations { if (options == null) throw new ArgumentNullException(nameof(options)); + IMqttChannel channel; switch (options.ChannelOptions) { case MqttClientTcpOptions _: { - return new MqttChannelAdapter(new MqttTcpChannel(options), new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttPacketWriter()), _logger); + channel = new MqttTcpChannel(options); + break; } case MqttClientWebSocketOptions webSocketOptions: { - return new MqttChannelAdapter(new MqttWebSocketChannel(webSocketOptions), new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttPacketWriter()), _logger); + channel = new MqttWebSocketChannel(webSocketOptions); + break; } default: @@ -36,6 +40,9 @@ namespace MQTTnet.Implementations throw new NotSupportedException(); } } + + var packetFormatterAdapter = new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttPacketWriter()); + return new MqttChannelAdapter(channel, packetFormatterAdapter, options.PacketInspector, _logger); } } } diff --git a/Source/MQTTnet/Implementations/MqttTcpChannel.cs b/Source/MQTTnet/Implementations/MqttTcpChannel.cs index 30208773..6e308109 100644 --- a/Source/MQTTnet/Implementations/MqttTcpChannel.cs +++ b/Source/MQTTnet/Implementations/MqttTcpChannel.cs @@ -77,7 +77,7 @@ namespace MQTTnet.Implementations cancellationToken.ThrowIfCancellationRequested(); var networkStream = socket.GetStream(); - + if (_tcpOptions.TlsOptions?.UseTls == true) { var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback); @@ -94,7 +94,7 @@ namespace MQTTnet.Implementations }; await sslStream.AuthenticateAsClientAsync(sslOptions, cancellationToken).ConfigureAwait(false); -#else +#else await sslStream.AuthenticateAsClientAsync(_tcpOptions.Server, LoadCertificates(), _tcpOptions.TlsOptions.SslProtocol, !_tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false); #endif } diff --git a/Source/MQTTnet/Implementations/MqttTcpServerAdapter.Uwp.cs b/Source/MQTTnet/Implementations/MqttTcpServerAdapter.Uwp.cs index 2010b5b0..dd164ba9 100644 --- a/Source/MQTTnet/Implementations/MqttTcpServerAdapter.Uwp.cs +++ b/Source/MQTTnet/Implementations/MqttTcpServerAdapter.Uwp.cs @@ -89,7 +89,7 @@ namespace MQTTnet.Implementations } } - using (var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket, clientCertificate, _options), new MqttPacketFormatterAdapter(new MqttPacketWriter()), _rootLogger)) + using (var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket, clientCertificate, _options), new MqttPacketFormatterAdapter(new MqttPacketWriter()), null, _rootLogger)) { await clientHandler(clientAdapter).ConfigureAwait(false); } diff --git a/Source/MQTTnet/Implementations/MqttTcpServerListener.cs b/Source/MQTTnet/Implementations/MqttTcpServerListener.cs index ef69890c..b7df3491 100644 --- a/Source/MQTTnet/Implementations/MqttTcpServerListener.cs +++ b/Source/MQTTnet/Implementations/MqttTcpServerListener.cs @@ -178,7 +178,11 @@ namespace MQTTnet.Implementations var clientHandler = ClientHandler; if (clientHandler != null) { - using (var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(stream, remoteEndPoint, clientCertificate), new MqttPacketFormatterAdapter(new MqttPacketWriter()), _rootLogger)) + using (var clientAdapter = new MqttChannelAdapter( + new MqttTcpChannel(stream, remoteEndPoint, clientCertificate), + new MqttPacketFormatterAdapter(new MqttPacketWriter()), + null, + _rootLogger)) { await clientHandler(clientAdapter).ConfigureAwait(false); } diff --git a/Source/MQTTnet/Internal/TestMqttChannel.cs b/Source/MQTTnet/Internal/TestMqttChannel.cs index fba12696..233a0cbb 100644 --- a/Source/MQTTnet/Internal/TestMqttChannel.cs +++ b/Source/MQTTnet/Internal/TestMqttChannel.cs @@ -6,7 +6,7 @@ using MQTTnet.Channel; namespace MQTTnet.Internal { - public class TestMqttChannel : IMqttChannel + public sealed class TestMqttChannel : IMqttChannel { readonly MemoryStream _stream; @@ -15,6 +15,11 @@ namespace MQTTnet.Internal _stream = stream; } + public TestMqttChannel(byte[] buffer) + { + _stream = new MemoryStream(buffer); + } + public string Endpoint { get; } = ""; public bool IsSecureConnection { get; } = false; diff --git a/Source/MQTTnet/MqttTopicFilterBuilder.cs b/Source/MQTTnet/MqttTopicFilterBuilder.cs index 712f25da..1cf8d8f6 100644 --- a/Source/MQTTnet/MqttTopicFilterBuilder.cs +++ b/Source/MQTTnet/MqttTopicFilterBuilder.cs @@ -13,6 +13,9 @@ namespace MQTTnet { MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce; string _topic; + bool? _noLocal; + bool? _retainAsPublished; + MqttRetainHandling? _retainHandling; public MqttTopicFilterBuilder WithTopic(string topic) { @@ -44,6 +47,24 @@ namespace MQTTnet return this; } + public MqttTopicFilterBuilder WithNoLocal(bool? value = true) + { + _noLocal = value; + return this; + } + + public MqttTopicFilterBuilder WithRetainAsPublished(bool? value = true) + { + _retainAsPublished = value; + return this; + } + + public MqttTopicFilterBuilder WithRetainHandling(MqttRetainHandling? value) + { + _retainHandling = value; + return this; + } + public MqttTopicFilter Build() { if (string.IsNullOrEmpty(_topic)) @@ -51,7 +72,14 @@ namespace MQTTnet throw new MqttProtocolViolationException("Topic is not set."); } - return new MqttTopicFilter { Topic = _topic, QualityOfServiceLevel = _qualityOfServiceLevel }; + return new MqttTopicFilter + { + Topic = _topic, + QualityOfServiceLevel = _qualityOfServiceLevel, + NoLocal = _noLocal, + RetainAsPublished = _retainAsPublished, + RetainHandling = _retainHandling + }; } } } diff --git a/Tests/MQTTnet.Benchmarks/ChannelAdapterBenchmark.cs b/Tests/MQTTnet.Benchmarks/ChannelAdapterBenchmark.cs index cbba6436..d6906f4b 100644 --- a/Tests/MQTTnet.Benchmarks/ChannelAdapterBenchmark.cs +++ b/Tests/MQTTnet.Benchmarks/ChannelAdapterBenchmark.cs @@ -43,7 +43,7 @@ namespace MQTTnet.Benchmarks var channel = new TestMqttChannel(_stream); - _channelAdapter = new MqttChannelAdapter(channel, serializer, new MqttNetLogger()); + _channelAdapter = new MqttChannelAdapter(channel, serializer, null, new MqttNetLogger()); } [Benchmark] diff --git a/Tests/MQTTnet.Benchmarks/SerializerBenchmark.cs b/Tests/MQTTnet.Benchmarks/SerializerBenchmark.cs index 3de03fae..ff567e87 100644 --- a/Tests/MQTTnet.Benchmarks/SerializerBenchmark.cs +++ b/Tests/MQTTnet.Benchmarks/SerializerBenchmark.cs @@ -9,6 +9,7 @@ using MQTTnet.Channel; using MQTTnet.Formatter; using MQTTnet.Formatter.V3; using BenchmarkDotNet.Jobs; +using MQTTnet.Diagnostics; namespace MQTTnet.Benchmarks { @@ -48,19 +49,13 @@ namespace MQTTnet.Benchmarks { var channel = new BenchmarkMqttChannel(_serializedPacket); var fixedHeader = new byte[2]; - var reader = new MqttPacketReader(channel); + var reader = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(new MqttPacketWriter()), null, new MqttNetLogger()); for (var i = 0; i < 10000; i++) { channel.Reset(); - var header = reader.ReadFixedHeaderAsync(fixedHeader, CancellationToken.None).GetAwaiter().GetResult().FixedHeader; - - var receivedPacket = new ReceivedMqttPacket( - header.Flags, - new MqttPacketBodyReader(_serializedPacket.Array, _serializedPacket.Count - header.RemainingLength, _serializedPacket.Array.Length), 0); - - _serializer.Decode(receivedPacket); + var header = reader.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult(); } } diff --git a/Tests/MQTTnet.Core.Tests/MqttPacketReader_Tests.cs b/Tests/MQTTnet.Core.Tests/MqttPacketReader_Tests.cs index c073cfc6..c160f4dc 100644 --- a/Tests/MQTTnet.Core.Tests/MqttPacketReader_Tests.cs +++ b/Tests/MQTTnet.Core.Tests/MqttPacketReader_Tests.cs @@ -1,23 +1,24 @@ -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using MQTTnet.Formatter; -using MQTTnet.Internal; +//using System.IO; +//using System.Threading; +//using System.Threading.Tasks; +//using Microsoft.VisualStudio.TestTools.UnitTesting; +//using MQTTnet.Formatter; +//using MQTTnet.Internal; -namespace MQTTnet.Tests -{ - [TestClass] - public class MqttPacketReader_Tests - { - [TestMethod] - public async Task MqttPacketReader_EmptyStream() - { - var fixedHeader = new byte[2]; - var reader = new MqttPacketReader(new TestMqttChannel(new MemoryStream())); - var readResult = await reader.ReadFixedHeaderAsync(fixedHeader, CancellationToken.None); +//namespace MQTTnet.Tests +//{ +// [TestClass] +// public class MqttPacketReader_Tests +// { +// [TestMethod] +// public async Task MqttPacketReader_EmptyStream() +// { +// var fixedHeader = new byte[2]; +// var reader = new MqttPacketReader(new TestMqttChannel(new MemoryStream())); +// var readResult = await reader.ReadFixedHeaderAsync(fixedHeader, CancellationToken.None); - Assert.IsTrue(readResult.ConnectionClosed); - } - } -} +// Assert.IsTrue(readResult.ConnectionClosed); +// } +// } +//} +// TODO: Fix diff --git a/Tests/MQTTnet.Core.Tests/MqttPacketSerializer_Tests.cs b/Tests/MQTTnet.Core.Tests/MqttPacketSerializer_Tests.cs index da3500be..e51c911c 100644 --- a/Tests/MQTTnet.Core.Tests/MqttPacketSerializer_Tests.cs +++ b/Tests/MQTTnet.Core.Tests/MqttPacketSerializer_Tests.cs @@ -6,6 +6,7 @@ using System.Text; using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; using MQTTnet.Adapter; +using MQTTnet.Diagnostics; using MQTTnet.Exceptions; using MQTTnet.Formatter; using MQTTnet.Formatter.V3; @@ -22,7 +23,7 @@ namespace MQTTnet.Tests [TestMethod] public void DetectVersionFromMqttConnectPacket() { - var p = new MqttConnectPacket + var packet = new MqttConnectPacket { ClientId = "XYZ", Password = Encoding.UTF8.GetBytes("PASS"), @@ -30,17 +31,26 @@ namespace MQTTnet.Tests KeepAlivePeriod = 123, CleanSession = true }; - var adapter = new MqttPacketFormatterAdapter(WriterFactory()); - - Assert.AreEqual(MqttProtocolVersion.V310, DeserializeAndDetectVersion(adapter, Serialize(p, MqttProtocolVersion.V310))); - Assert.AreEqual(MqttProtocolVersion.V311, DeserializeAndDetectVersion(adapter, Serialize(p, MqttProtocolVersion.V311))); - Assert.AreEqual(MqttProtocolVersion.V500, DeserializeAndDetectVersion(adapter, Serialize(p, MqttProtocolVersion.V500))); - + + Assert.AreEqual( + MqttProtocolVersion.V310, + DeserializeAndDetectVersion(new MqttPacketFormatterAdapter(new MqttPacketWriter()), Serialize(packet, MqttProtocolVersion.V310))); + + Assert.AreEqual( + MqttProtocolVersion.V311, + DeserializeAndDetectVersion(new MqttPacketFormatterAdapter(new MqttPacketWriter()), Serialize(packet, MqttProtocolVersion.V311))); + + Assert.AreEqual( + MqttProtocolVersion.V500, + DeserializeAndDetectVersion(new MqttPacketFormatterAdapter(new MqttPacketWriter()), Serialize(packet, MqttProtocolVersion.V500))); + + var adapter = new MqttPacketFormatterAdapter(new MqttPacketWriter()); + var ex = Assert.ThrowsException(() => DeserializeAndDetectVersion(adapter, WriterFactory().AddMqttHeader(MqttControlPacketType.Connect, new byte[0]))); Assert.AreEqual("CONNECT packet must have at least 7 bytes.", ex.Message); ex = Assert.ThrowsException(() => DeserializeAndDetectVersion(adapter, WriterFactory().AddMqttHeader(MqttControlPacketType.Connect, new byte[7]))); Assert.AreEqual("Protocol '' not supported.", ex.Message); - ex = Assert.ThrowsException(() => DeserializeAndDetectVersion(adapter, WriterFactory().AddMqttHeader(MqttControlPacketType.Connect, new byte[] { 255, 255, 0,0,0,0,0 }))); + ex = Assert.ThrowsException(() => DeserializeAndDetectVersion(adapter, WriterFactory().AddMqttHeader(MqttControlPacketType.Connect, new byte[] { 255, 255, 0, 0, 0, 0, 0 }))); Assert.AreEqual("Expected at least 65537 bytes but there are only 7 bytes", ex.Message); } @@ -205,24 +215,28 @@ namespace MQTTnet.Tests Payload = payload }; - var buffer = serializer.Encode(publishPacket); - var testChannel = new TestMqttChannel(new MemoryStream(buffer.Array, buffer.Offset, buffer.Count)); - var header = new MqttPacketReader(testChannel).ReadFixedHeaderAsync( - new byte[2], - CancellationToken.None).GetAwaiter().GetResult().FixedHeader; + var publishPacketCopy = Roundtrip(publishPacket); - var eof = buffer.Offset + buffer.Count; + //var buffer = serializer.Encode(publishPacket); + //var testChannel = new TestMqttChannel(new MemoryStream(buffer.Array, buffer.Offset, buffer.Count)); - var receivedPacket = new ReceivedMqttPacket( - header.Flags, - new MqttPacketBodyReader(buffer.Array, eof - header.RemainingLength, buffer.Count + buffer.Offset), - 0); - var packet = (MqttPublishPacket)serializer.Decode(receivedPacket); + //var header = new MqttPacketReader(testChannel).ReadFixedHeaderAsync( + // new byte[2], + // CancellationToken.None).GetAwaiter().GetResult().FixedHeader; - Assert.AreEqual(publishPacket.Topic, packet.Topic); - Assert.IsTrue(publishPacket.Payload.SequenceEqual(packet.Payload)); + //var eof = buffer.Offset + buffer.Count; + + //var receivedPacket = new ReceivedMqttPacket( + // header.Flags, + // new MqttPacketBodyReader(buffer.Array, eof - header.RemainingLength, buffer.Count + buffer.Offset), + // 0); + + //var packet = (MqttPublishPacket)serializer.Decode(receivedPacket); + + Assert.AreEqual(publishPacket.Topic, publishPacketCopy.Topic); + Assert.IsTrue(publishPacket.Payload.SequenceEqual(publishPacketCopy.Payload)); } [TestMethod] @@ -262,7 +276,7 @@ namespace MQTTnet.Tests [TestMethod] public void SerializeV500_MqttPublishPacket() { - var prop = new MqttPublishPacketProperties {UserProperties = new List()}; + var prop = new MqttPublishPacketProperties { UserProperties = new List() }; prop.ResponseTopic = "/Response"; @@ -581,15 +595,14 @@ namespace MQTTnet.Tests DeserializeAndCompare(p, "sAIAew=="); } - private void SerializeAndCompare(MqttBasePacket packet, string expectedBase64Value, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311) + void SerializeAndCompare(MqttBasePacket packet, string expectedBase64Value, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311) { Assert.AreEqual(expectedBase64Value, Convert.ToBase64String(Serialize(packet, protocolVersion))); } - private byte[] Serialize(MqttBasePacket packet, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311) + byte[] Serialize(MqttBasePacket packet, MqttProtocolVersion protocolVersion) { - var serializer = MqttPacketFormatterAdapter.GetMqttPacketFormatter(protocolVersion, WriterFactory()); - return Join(serializer.Encode(packet)); + return MqttPacketFormatterAdapter.GetMqttPacketFormatter(protocolVersion, WriterFactory()).Encode(packet).ToArray(); } protected virtual IMqttPacketWriter WriterFactory() @@ -602,83 +615,92 @@ namespace MQTTnet.Tests return new MqttPacketBodyReader(data, 0, data.Length); } - private void DeserializeAndCompare(MqttBasePacket packet, string expectedBase64Value, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311) + void DeserializeAndCompare(MqttBasePacket packet, string expectedBase64Value, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311) { var writer = WriterFactory(); var serializer = MqttPacketFormatterAdapter.GetMqttPacketFormatter(protocolVersion, writer); - var buffer1 = serializer.Encode(packet); - using (var headerStream = new MemoryStream(Join(buffer1))) + using (var headerStream = new MemoryStream(buffer1.ToArray())) { var channel = new TestMqttChannel(headerStream); - var fixedHeader = new byte[2]; - var header = new MqttPacketReader(channel).ReadFixedHeaderAsync(fixedHeader, CancellationToken.None).GetAwaiter().GetResult().FixedHeader; + var adapter = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(protocolVersion, writer), null, new MqttNetLogger()); + var receivedPacket = adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult(); - using (var bodyStream = new MemoryStream(Join(buffer1), (int)headerStream.Position, header.RemainingLength)) - { - var reader = ReaderFactory(bodyStream.ToArray()); - var deserializedPacket = serializer.Decode(new ReceivedMqttPacket(header.Flags, reader, 0)); - var buffer2 = serializer.Encode(deserializedPacket); + var buffer2 = serializer.Encode(receivedPacket); - Assert.AreEqual(expectedBase64Value, Convert.ToBase64String(Join(buffer2))); - } + Assert.AreEqual(expectedBase64Value, Convert.ToBase64String(buffer2.ToArray())); + + //adapter.ReceivePacketAsync(CancellationToken.None); + //var fixedHeader = new byte[2]; + //var header = new MqttPacketReader(channel).ReadFixedHeaderAsync(fixedHeader, CancellationToken.None).GetAwaiter().GetResult().FixedHeader; + + //using (var bodyStream = new MemoryStream(Join(buffer1), (int)headerStream.Position, header.RemainingLength)) + //{ + // var reader = ReaderFactory(bodyStream.ToArray()); + // var deserializedPacket = serializer.Decode(new ReceivedMqttPacket(header.Flags, reader, 0)); + // var buffer2 = serializer.Encode(deserializedPacket); + + // Assert.AreEqual(expectedBase64Value, Convert.ToBase64String(Join(buffer2))); + //} } } - private T Roundtrip(T packet, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311) - where T : MqttBasePacket + TPacket Roundtrip(TPacket packet, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311) + where TPacket : MqttBasePacket { var writer = WriterFactory(); - var serializer = MqttPacketFormatterAdapter.GetMqttPacketFormatter(protocolVersion, writer); + var buffer = serializer.Encode(packet); - var buffer1 = serializer.Encode(packet); + var channel = new TestMqttChannel(buffer.ToArray()); + var adapter = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(protocolVersion, writer), null, new MqttNetLogger()); + return (TPacket)adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult(); - using (var headerStream = new MemoryStream(Join(buffer1))) - { - var channel = new TestMqttChannel(headerStream); - var fixedHeader = new byte[2]; + //using (var headerStream = new MemoryStream(buffer1.ToArray())) + //{ - var header = new MqttPacketReader(channel).ReadFixedHeaderAsync(fixedHeader, CancellationToken.None).GetAwaiter().GetResult().FixedHeader; - using (var bodyStream = new MemoryStream(Join(buffer1), (int)headerStream.Position, (int)header.RemainingLength)) - { - var reader = ReaderFactory(bodyStream.ToArray()); - return (T)serializer.Decode(new ReceivedMqttPacket(header.Flags, reader, 0)); - } - } + + + // //var fixedHeader = new byte[2]; + + // //var header = new MqttPacketReader(channel).ReadFixedHeaderAsync(fixedHeader, CancellationToken.None).GetAwaiter().GetResult().FixedHeader; + + // //using (var bodyStream = new MemoryStream(Join(buffer1), (int)headerStream.Position, (int)header.RemainingLength)) + // //{ + // // var reader = ReaderFactory(bodyStream.ToArray()); + // // return (T)serializer.Decode(new ReceivedMqttPacket(header.Flags, reader, 0)); + // //} + //} } - private MqttProtocolVersion DeserializeAndDetectVersion(MqttPacketFormatterAdapter adapter, byte[] buffer) + MqttProtocolVersion DeserializeAndDetectVersion(MqttPacketFormatterAdapter packetFormatterAdapter, byte[] buffer) { - using (var headerStream = new MemoryStream(buffer)) - { - var channel = new TestMqttChannel(headerStream); - var fixedHeader = new byte[2]; + var channel = new TestMqttChannel(buffer); + var adapter = new MqttChannelAdapter(channel, packetFormatterAdapter, null, new MqttNetLogger()); - var header = new MqttPacketReader(channel).ReadFixedHeaderAsync(fixedHeader, CancellationToken.None).GetAwaiter().GetResult().FixedHeader; + adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult(); + return packetFormatterAdapter.ProtocolVersion; - using (var bodyStream = new MemoryStream(buffer, (int)headerStream.Position, (int)header.RemainingLength)) - { - var reader = ReaderFactory(bodyStream.ToArray()); - var packet = new ReceivedMqttPacket(header.Flags, reader, 0); - adapter.DetectProtocolVersion(packet); - return adapter.ProtocolVersion; - } - } - } + //using (var headerStream = new MemoryStream(buffer)) + //{ - private static byte[] Join(params ArraySegment[] chunks) - { - var buffer = new MemoryStream(); - foreach (var chunk in chunks) - { - buffer.Write(chunk.Array, chunk.Offset, chunk.Count); - } - return buffer.ToArray(); + + + // //var fixedHeader = new byte[2]; + // //var header = new MqttPacketReader(channel).ReadFixedHeaderAsync(fixedHeader, CancellationToken.None).GetAwaiter().GetResult().FixedHeader; + + // //using (var bodyStream = new MemoryStream(buffer, (int)headerStream.Position, (int)header.RemainingLength)) + // //{ + // // var reader = ReaderFactory(bodyStream.ToArray()); + // // var packet = new ReceivedMqttPacket(header.Flags, reader, 0); + // // packetFormatterAdapter.DetectProtocolVersion(packet); + // // return adapter.ProtocolVersion; + // //} + //} } } } From 2abcf6d8bc2e6bf3a783e3b770d410915f14f358 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 25 Jan 2021 22:27:45 +0100 Subject: [PATCH 07/29] Update docs. --- Build/MQTTnet.AspNetCore.nuspec | 15 ++++++++---- Build/MQTTnet.Extensions.ManagedClient.nuspec | 5 +++- Build/MQTTnet.Extensions.Rpc.nuspec | 15 +++++++----- Build/MQTTnet.Extensions.WebSocket4Net.nuspec | 15 +++++++----- Build/MQTTnet.nuspec | 23 ++++++++++--------- 5 files changed, 45 insertions(+), 28 deletions(-) diff --git a/Build/MQTTnet.AspNetCore.nuspec b/Build/MQTTnet.AspNetCore.nuspec index 53fb07ec..c2641907 100644 --- a/Build/MQTTnet.AspNetCore.nuspec +++ b/Build/MQTTnet.AspNetCore.nuspec @@ -19,10 +19,14 @@ - + + + + + @@ -33,10 +37,13 @@ + + + - - + + - + diff --git a/Build/MQTTnet.Extensions.ManagedClient.nuspec b/Build/MQTTnet.Extensions.ManagedClient.nuspec index d7de8f47..7691919d 100644 --- a/Build/MQTTnet.Extensions.ManagedClient.nuspec +++ b/Build/MQTTnet.Extensions.ManagedClient.nuspec @@ -13,7 +13,7 @@ This is an extension library which provides a managed MQTT client with additional features using MQTTnet. For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/). Copyright Christian Kratky 2016-2020 - MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor + MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor @@ -26,6 +26,9 @@ + + + diff --git a/Build/MQTTnet.Extensions.Rpc.nuspec b/Build/MQTTnet.Extensions.Rpc.nuspec index 109665af..20ab9ef4 100644 --- a/Build/MQTTnet.Extensions.Rpc.nuspec +++ b/Build/MQTTnet.Extensions.Rpc.nuspec @@ -13,7 +13,7 @@ This is an extension library which allows executing synchronous device calls including a response using MQTTnet. For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/). Copyright Christian Kratky 2016-2020 - MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor + MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor @@ -26,20 +26,23 @@ + + + - + - + - + - + - + diff --git a/Build/MQTTnet.Extensions.WebSocket4Net.nuspec b/Build/MQTTnet.Extensions.WebSocket4Net.nuspec index 58a52295..3def4dab 100644 --- a/Build/MQTTnet.Extensions.WebSocket4Net.nuspec +++ b/Build/MQTTnet.Extensions.WebSocket4Net.nuspec @@ -13,7 +13,7 @@ This is an extension library which allows using _WebSocket4Net_ as transport for MQTTnet clients. For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/). Copyright Christian Kratky 2016-2020 - MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor + MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor @@ -41,20 +41,23 @@ + + + - + - + - + - + - + diff --git a/Build/MQTTnet.nuspec b/Build/MQTTnet.nuspec index a5aad211..cea9db25 100644 --- a/Build/MQTTnet.nuspec +++ b/Build/MQTTnet.nuspec @@ -18,6 +18,7 @@ * [Core] Aligned the format of some log messages. * [Client] Fixed an issue with connection state detection (thanks to @tobiasfrick). * [Client] Fixed an issue when receiving partial QoS 2 packets after a reconnect. +* [Client] Added new API for raw packet inspection. * [ManagedClient] Removed _AutoReconnect_ since this does no longer work (BREAKING CHANGE!). * [RpcClient] Moved the RPC topic validation to the topic generation strategy. * [RcpClient] Added interface for _MqttRpcClient_. @@ -30,13 +31,13 @@ Git commit: $gitCommit Copyright Christian Kratky 2016-2020 - MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor + MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor - + @@ -70,28 +71,28 @@ Git commit: $gitCommit - + - - + + - + - + - + - + - + - + From df45540d6bb8da41191e9916981aef71f6b24647 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 25 Jan 2021 22:28:01 +0100 Subject: [PATCH 08/29] Update docs. --- Build/MQTTnet.Extensions.ManagedClient.nuspec | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Build/MQTTnet.Extensions.ManagedClient.nuspec b/Build/MQTTnet.Extensions.ManagedClient.nuspec index 7691919d..b3322947 100644 --- a/Build/MQTTnet.Extensions.ManagedClient.nuspec +++ b/Build/MQTTnet.Extensions.ManagedClient.nuspec @@ -30,19 +30,19 @@ - + - + - + - + - + From f8eaf025165544b26b6e2bd4928cf46719c75157 Mon Sep 17 00:00:00 2001 From: Muneeb Majeed Date: Tue, 26 Jan 2021 17:18:52 -0500 Subject: [PATCH 09/29] Allow to respond with a reason code in PUBACK/PUBREC --- Source/MQTTnet/Client/MqttClient.cs | 27 ++++++++++--------- ...MqttApplicationMessageReceivedEventArgs.cs | 5 +++- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Source/MQTTnet/Client/MqttClient.cs b/Source/MQTTnet/Client/MqttClient.cs index a6901fe4..a415c438 100644 --- a/Source/MQTTnet/Client/MqttClient.cs +++ b/Source/MQTTnet/Client/MqttClient.cs @@ -637,26 +637,26 @@ namespace MQTTnet.Client } else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce) { - if (await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false)) + var reasonCode = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false); + if (reasonCode != null && Enum.IsDefined(typeof(MqttPubAckReasonCode), reasonCode.Value)) { await SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier, - ReasonCode = MqttPubAckReasonCode.Success + ReasonCode = (MqttPubAckReasonCode)reasonCode.Value }, cancellationToken).ConfigureAwait(false); } } else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce) { - if (await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false)) + var reasonCode = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false); + if (reasonCode != null && Enum.IsDefined(typeof(MqttPubRecReasonCode), reasonCode.Value)) { - var pubRecPacket = new MqttPubRecPacket + await SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier, - ReasonCode = MqttPubRecReasonCode.Success - }; - - await SendAsync(pubRecPacket, cancellationToken).ConfigureAwait(false); + ReasonCode = (MqttPubRecReasonCode)reasonCode.Value + }, cancellationToken).ConfigureAwait(false); } } else @@ -756,8 +756,9 @@ namespace MQTTnet.Client return _adapter.PacketFormatterAdapter.DataConverter.CreatePublishResult(pubRecPacket, pubCompPacket); } - async Task HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket) + async Task HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket) { + int? reasonCode = 0; var applicationMessage = _adapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket); var handler = ApplicationMessageReceivedHandler; @@ -765,10 +766,12 @@ namespace MQTTnet.Client { var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage); await handler.HandleApplicationMessageReceivedAsync(eventArgs).ConfigureAwait(false); - return !eventArgs.ProcessingFailed; + if (eventArgs.ProcessingFailed) + { + reasonCode = eventArgs.ReasonCode; + } } - - return true; + return reasonCode; } async Task WaitForTaskAsync(Task task, Task sender) diff --git a/Source/MQTTnet/MqttApplicationMessageReceivedEventArgs.cs b/Source/MQTTnet/MqttApplicationMessageReceivedEventArgs.cs index ed46305c..8bbc571f 100644 --- a/Source/MQTTnet/MqttApplicationMessageReceivedEventArgs.cs +++ b/Source/MQTTnet/MqttApplicationMessageReceivedEventArgs.cs @@ -1,4 +1,5 @@ -using System; +using MQTTnet.Protocol; +using System; namespace MQTTnet { @@ -15,5 +16,7 @@ namespace MQTTnet public MqttApplicationMessage ApplicationMessage { get; } public bool ProcessingFailed { get; set; } + + public int? ReasonCode { get; set; } } } From b1bf7698aa476e96801b95ecb5d95f9d71230abc Mon Sep 17 00:00:00 2001 From: Philip Schill Date: Fri, 29 Jan 2021 17:37:47 +0100 Subject: [PATCH 10/29] The topic filter comparer now checks for e.g. "foo/" matching "foo/#". --- Source/MQTTnet/Server/MqttTopicFilterComparer.cs | 7 +++++++ Tests/MQTTnet.Core.Tests/TopicFilterComparer_Tests.cs | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/Source/MQTTnet/Server/MqttTopicFilterComparer.cs b/Source/MQTTnet/Server/MqttTopicFilterComparer.cs index 4ba614d4..5b104b48 100644 --- a/Source/MQTTnet/Server/MqttTopicFilterComparer.cs +++ b/Source/MQTTnet/Server/MqttTopicFilterComparer.cs @@ -31,6 +31,13 @@ namespace MQTTnet.Server { return true; } + // Check for e.g. foo/ matching foo/# + if (sPos == sLen - 2 + && filter[sPos] == LevelSeparator + && filter[sPos + 1] == MultiLevelWildcard) + { + return true; + } } sPos++; diff --git a/Tests/MQTTnet.Core.Tests/TopicFilterComparer_Tests.cs b/Tests/MQTTnet.Core.Tests/TopicFilterComparer_Tests.cs index 35e85047..b1de7c8d 100644 --- a/Tests/MQTTnet.Core.Tests/TopicFilterComparer_Tests.cs +++ b/Tests/MQTTnet.Core.Tests/TopicFilterComparer_Tests.cs @@ -54,6 +54,12 @@ namespace MQTTnet.Tests CompareAndAssert("A/B/C/D", "A/C/#", false); } + [TestMethod] + public void TopicFilterComparer_EndMultipleLevelsWildcardMatchEmptyLevel() + { + CompareAndAssert("A/", "A/#", true); + } + [TestMethod] public void TopicFilterComparer_AllLevelsWildcardMatch() { From f3f1d81b522d1e6d31cc3472684b2bc45552c1b2 Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 29 Jan 2021 22:44:18 +0100 Subject: [PATCH 11/29] Update docs. --- Build/MQTTnet.nuspec | 1 + Source/MQTTnet/Client/MqttClient.cs | 28 +++++++++---------- Source/MQTTnet/Formatter/MqttPacketWriter.cs | 10 +++---- ...MqttApplicationMessageReceivedEventArgs.cs | 5 ++-- ...qttApplicationMessageReceivedReasonCode.cs | 15 ++++++++++ Source/MQTTnet/MqttTopicFilterBuilder.cs | 4 +-- 6 files changed, 38 insertions(+), 25 deletions(-) create mode 100644 Source/MQTTnet/MqttApplicationMessageReceivedReasonCode.cs diff --git a/Build/MQTTnet.nuspec b/Build/MQTTnet.nuspec index cea9db25..b3a4c5ba 100644 --- a/Build/MQTTnet.nuspec +++ b/Build/MQTTnet.nuspec @@ -27,6 +27,7 @@ * [Server] Fixed bug in PubRel packet generation (MQTTv5 only). * [Server] Improved message processing performance (+ ~5%). * [Server] Fix wrong usage of client session items for undelivered messages. +* [Server] Allow to respond with a reason code in PUBACK/PUBREC (thanks to @muneebmajeed). Git commit: $gitCommit diff --git a/Source/MQTTnet/Client/MqttClient.cs b/Source/MQTTnet/Client/MqttClient.cs index a415c438..6b454d90 100644 --- a/Source/MQTTnet/Client/MqttClient.cs +++ b/Source/MQTTnet/Client/MqttClient.cs @@ -637,25 +637,27 @@ namespace MQTTnet.Client } else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce) { - var reasonCode = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false); - if (reasonCode != null && Enum.IsDefined(typeof(MqttPubAckReasonCode), reasonCode.Value)) + var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false); + + if (!eventArgs.ProcessingFailed) { await SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier, - ReasonCode = (MqttPubAckReasonCode)reasonCode.Value + ReasonCode = (MqttPubAckReasonCode)eventArgs.ReasonCode }, cancellationToken).ConfigureAwait(false); } } else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce) { - var reasonCode = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false); - if (reasonCode != null && Enum.IsDefined(typeof(MqttPubRecReasonCode), reasonCode.Value)) + var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false); + + if (!eventArgs.ProcessingFailed) { await SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier, - ReasonCode = (MqttPubRecReasonCode)reasonCode.Value + ReasonCode = (MqttPubRecReasonCode)eventArgs.ReasonCode }, cancellationToken).ConfigureAwait(false); } } @@ -678,7 +680,7 @@ namespace MQTTnet.Client { if (!_packetDispatcher.TryDispatch(pubRecPacket)) { - // The packet is unknown. Probably due to a restart of the client. + // The packet is unknown. Probably due to a restart of the client. // So wen send this to the server to trigger a full resend of the message. return SendAsync(new MqttPubRelPacket { @@ -756,22 +758,18 @@ namespace MQTTnet.Client return _adapter.PacketFormatterAdapter.DataConverter.CreatePublishResult(pubRecPacket, pubCompPacket); } - async Task HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket) + async Task HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket) { - int? reasonCode = 0; var applicationMessage = _adapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket); + var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage); var handler = ApplicationMessageReceivedHandler; if (handler != null) { - var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage); await handler.HandleApplicationMessageReceivedAsync(eventArgs).ConfigureAwait(false); - if (eventArgs.ProcessingFailed) - { - reasonCode = eventArgs.ReasonCode; - } } - return reasonCode; + + return eventArgs; } async Task WaitForTaskAsync(Task task, Task sender) diff --git a/Source/MQTTnet/Formatter/MqttPacketWriter.cs b/Source/MQTTnet/Formatter/MqttPacketWriter.cs index a51ba618..c7ab8ac1 100644 --- a/Source/MQTTnet/Formatter/MqttPacketWriter.cs +++ b/Source/MQTTnet/Formatter/MqttPacketWriter.cs @@ -97,7 +97,7 @@ namespace MQTTnet.Formatter _buffer[_offset] = (byte)(bufferSize >> 8); _buffer[_offset + 1] = (byte)bufferSize; - + Encoding.UTF8.GetBytes(value, 0, value.Length, _buffer, _offset + 2); IncreasePosition(bufferSize + 2); @@ -121,7 +121,7 @@ namespace MQTTnet.Formatter _buffer[_offset] = (byte)(value.Length >> 8); _buffer[_offset + 1] = (byte)value.Length; - + Array.Copy(value, 0, _buffer, _offset + 2, value.Length); IncreasePosition(value.Length + 2); } @@ -201,8 +201,8 @@ namespace MQTTnet.Formatter { // This method frees the used memory by shrinking the buffer. This is required because the buffer // is used across several messages. In general this is not a big issue because subsequent Ping packages - // have the same size but a very big publish package with 100 MB of payload will increase the buffer - // a lot and the size will never reduced. So this method tries to find a size which can be held in + // have the same size but a very big publish package with 100 MB of payload will increase the buffer + // a lot and the size will never reduced. So this method tries to find a size which can be held in // memory for a long time without causing troubles. if (_buffer.Length < MaxBufferSize) @@ -212,7 +212,7 @@ namespace MQTTnet.Formatter Array.Resize(ref _buffer, MaxBufferSize); } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] void EnsureAdditionalCapacity(int additionalCapacity) { diff --git a/Source/MQTTnet/MqttApplicationMessageReceivedEventArgs.cs b/Source/MQTTnet/MqttApplicationMessageReceivedEventArgs.cs index 8bbc571f..a27f9c7b 100644 --- a/Source/MQTTnet/MqttApplicationMessageReceivedEventArgs.cs +++ b/Source/MQTTnet/MqttApplicationMessageReceivedEventArgs.cs @@ -1,5 +1,4 @@ -using MQTTnet.Protocol; -using System; +using System; namespace MQTTnet { @@ -17,6 +16,6 @@ namespace MQTTnet public bool ProcessingFailed { get; set; } - public int? ReasonCode { get; set; } + public MqttApplicationMessageReceivedReasonCode ReasonCode { get; set; } = MqttApplicationMessageReceivedReasonCode.Success; } } diff --git a/Source/MQTTnet/MqttApplicationMessageReceivedReasonCode.cs b/Source/MQTTnet/MqttApplicationMessageReceivedReasonCode.cs new file mode 100644 index 00000000..b192d311 --- /dev/null +++ b/Source/MQTTnet/MqttApplicationMessageReceivedReasonCode.cs @@ -0,0 +1,15 @@ +namespace MQTTnet +{ + public enum MqttApplicationMessageReceivedReasonCode + { + Success = 0, + NoMatchingSubscribers = 16, + UnspecifiedError = 128, + ImplementationSpecificError = 131, + NotAuthorized = 135, + TopicNameInvalid = 144, + PacketIdentifierInUse = 145, + QuotaExceeded = 151, + PayloadFormatInvalid = 153 + } +} \ No newline at end of file diff --git a/Source/MQTTnet/MqttTopicFilterBuilder.cs b/Source/MQTTnet/MqttTopicFilterBuilder.cs index 1cf8d8f6..3bb75abc 100644 --- a/Source/MQTTnet/MqttTopicFilterBuilder.cs +++ b/Source/MQTTnet/MqttTopicFilterBuilder.cs @@ -64,7 +64,7 @@ namespace MQTTnet _retainHandling = value; return this; } - + public MqttTopicFilter Build() { if (string.IsNullOrEmpty(_topic)) @@ -74,7 +74,7 @@ namespace MQTTnet return new MqttTopicFilter { - Topic = _topic, + Topic = _topic, QualityOfServiceLevel = _qualityOfServiceLevel, NoLocal = _noLocal, RetainAsPublished = _retainAsPublished, From b88924bce50671b4270282f4ca7ec10e429eaa49 Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 29 Jan 2021 23:10:40 +0100 Subject: [PATCH 12/29] Fixed wrong buffer size calculation. --- Source/MQTTnet/Formatter/MqttPacketWriter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/MQTTnet/Formatter/MqttPacketWriter.cs b/Source/MQTTnet/Formatter/MqttPacketWriter.cs index c7ab8ac1..48d95783 100644 --- a/Source/MQTTnet/Formatter/MqttPacketWriter.cs +++ b/Source/MQTTnet/Formatter/MqttPacketWriter.cs @@ -14,7 +14,8 @@ namespace MQTTnet.Formatter public sealed class MqttPacketWriter : IMqttPacketWriter { public static int InitialBufferSize = 4096; - public static int MaxBufferSize = 4096 + 4; + + public static int MaxBufferSize = 4096 * 4; byte[] _buffer = new byte[InitialBufferSize]; From b103718bd9751c060e956d0b7c8e4b9ce4f1745c Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 29 Jan 2021 23:17:16 +0100 Subject: [PATCH 13/29] Add Unit Tests. --- .../TopicFilterComparer_Tests.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Tests/MQTTnet.Core.Tests/TopicFilterComparer_Tests.cs b/Tests/MQTTnet.Core.Tests/TopicFilterComparer_Tests.cs index b1de7c8d..5fb3d44e 100644 --- a/Tests/MQTTnet.Core.Tests/TopicFilterComparer_Tests.cs +++ b/Tests/MQTTnet.Core.Tests/TopicFilterComparer_Tests.cs @@ -6,6 +6,20 @@ namespace MQTTnet.Tests [TestClass] public class TopicFilterComparer_Tests { + [TestMethod] + public void TopicFilterComparer_Plus_Match_With_Separator_Only() + { + //A Topic Name or Topic Filter consisting only of the ‘/’ character is valid + CompareAndAssert("A", "+", true); + } + + [TestMethod] + public void TopicFilterComparer_Hash_Match_With_Separator_Only() + { + //A Topic Name or Topic Filter consisting only of the ‘/’ character is valid + CompareAndAssert("/", "#", true); + } + [TestMethod] public void TopicFilterComparer_DirectMatch() { From c91a1f8572893e597c0bf3ae58385b52a164a4bb Mon Sep 17 00:00:00 2001 From: Christian Date: Sat, 30 Jan 2021 13:12:41 +0100 Subject: [PATCH 14/29] Update solution settings. --- .editorconfig | 83 +++++++++++++++++++++++++++++++++++++++++++++++++-- README.md | 28 +++++++++++++---- 2 files changed, 104 insertions(+), 7 deletions(-) diff --git a/.editorconfig b/.editorconfig index 6555efd5..c4b05393 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,5 +1,84 @@ -[*.cs] +[*.cs] # CA1031: Do not catch general exception types -dotnet_diagnostic.CA1031.severity = none + +# Microsoft .NET properties +csharp_new_line_before_members_in_object_initializers = false +csharp_preferred_modifier_order = public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion +csharp_style_var_elsewhere = true:suggestion +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +dotnet_naming_rule.constants_rule.severity = warning +dotnet_naming_rule.constants_rule.style = upper_camel_case_style +dotnet_naming_rule.constants_rule.symbols = constants_symbols +dotnet_naming_rule.public_fields_rule.severity = warning +dotnet_naming_rule.public_fields_rule.style = lower_camel_case_style +dotnet_naming_rule.public_fields_rule.symbols = public_fields_symbols +dotnet_naming_rule.static_readonly_rule.severity = warning +dotnet_naming_rule.static_readonly_rule.style = upper_camel_case_style +dotnet_naming_rule.static_readonly_rule.symbols = static_readonly_symbols +dotnet_naming_style.lower_camel_case_style.capitalization = camel_case +dotnet_naming_style.lower_camel_case_style.required_prefix = _ +dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case +dotnet_naming_symbols.constants_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected +dotnet_naming_symbols.constants_symbols.applicable_kinds = field +dotnet_naming_symbols.constants_symbols.required_modifiers = const +dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected +dotnet_naming_symbols.public_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected +dotnet_naming_symbols.static_readonly_symbols.applicable_kinds = field +dotnet_naming_symbols.static_readonly_symbols.required_modifiers = static,readonly +dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none +dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none +dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion +dotnet_style_qualification_for_event = false:suggestion +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_property = false:suggestion + +# ReSharper properties +resharper_autodetect_indent_settings = true +resharper_default_private_modifier = implicit +resharper_place_accessorholder_attribute_on_same_line = false +resharper_use_indent_from_vs = false + +# ReSharper inspection severities +resharper_arrange_redundant_parentheses_highlighting = hint +resharper_arrange_this_qualifier_highlighting = hint +resharper_built_in_type_reference_style_for_member_access_highlighting = hint +resharper_built_in_type_reference_style_highlighting = hint +resharper_redundant_base_qualifier_highlighting = warning +resharper_suggest_var_or_type_built_in_types_highlighting = hint +resharper_suggest_var_or_type_elsewhere_highlighting = hint +resharper_suggest_var_or_type_simple_types_highlighting = hint +resharper_web_config_module_not_resolved_highlighting = warning +resharper_web_config_type_not_resolved_highlighting = warning +resharper_web_config_wrong_module_highlighting = warning + +[*] +charset = utf-8 +end_of_line = crlf +trim_trailing_whitespace = false +insert_final_newline = false +indent_style = space +indent_size = 4 + +[{*.har,*.inputactions,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,jest.config}] +indent_style = space +indent_size = 2 + +[{*.yaml,*.yml}] +indent_style = space +indent_size = 2 + +[{tsconfig.app.json,tsconfig.e2e.json,tsconfig.json,tsconfig.lib.json,tsconfig.spec.json}] +indent_style = space +indent_size = 2 + +[*.{appxmanifest,asax,ascx,aspx,axaml,build,cg,cginc,compute,cs,cshtml,dtd,hlsl,hlsli,hlslinc,master,nuspec,paml,razor,resw,resx,shader,skin,usf,ush,vb,xaml,xamlx,xoml,xsd}] +indent_style = space +indent_size = 4 +tab_width = 4 diff --git a/README.md b/README.md index 5b4bf7f3..31e4caad 100644 --- a/README.md +++ b/README.md @@ -64,15 +64,32 @@ _MQTTnet Server_ is a standalone cross platform MQTT server (like mosquitto) bas ## Supported frameworks +* .NET 5.0+ * .NET Standard 1.3+ * .NET Core 1.1+ * .NET Core App 1.1+ -* .NET Framework 4.5.2+ (x86, x64, AnyCPU) +* .NET Framework 4.5.2+ * Mono 5.2+ -* Universal Windows Platform (UWP) 10.0.10240+ (x86, x64, ARM, AnyCPU, Windows 10 IoT Core) +* Universal Windows Platform (UWP) 10.0.10240+ (Windows 10 IoT Core) * Xamarin.Android 7.5+ * Xamarin.iOS 10.14+ -* Blazor WebAssembly 3.2.0 +* Blazor WebAssembly 3.2.0+ + +## Supported platforms + +* x86 +* x64 +* AnyCPU +* ARM + +## Supported OS + +* Windows +* Windows 10 IoT Core +* Linux (Ubuntu, Raspbian etc.) +* macOS +* Android +* iOS ## Supported MQTT versions @@ -97,11 +114,12 @@ Also additions and updates in the Wiki are welcome. This library is used in the following projects: -* Azure Functions MQTT Bindings, [CaseOnline.Azure.WebJobs.Extensions.Mqtt](https://github.com/keesschollaart81/CaseOnline.Azure.WebJobs.Extensions.Mqtt/) +* Azure Functions MQTT Bindings, ) * HA4IoT (Open Source Home Automation system for .NET, ) * MQTT Client Rx (Wrapper for Reactive Extensions, ) * MQTT Client Rx (Managed Client Wrapper for Reactive Extensions, ) * MQTT Tester (MQTT client test app for [Android](https://play.google.com/store/apps/details?id=com.liveowl.mqtttester) and [iOS](https://itunes.apple.com/us/app/mqtt-tester/id1278621826?mt=8)) +* MQTTnet App (Cross platform client application for MQTT debugging, inspection etc., ) * Wirehome.Core (Open Source Home Automation system for .NET Core, ) Further projects using this project can be found under https://github.com/chkr1011/MQTTnet/network/dependents. @@ -112,7 +130,7 @@ If you use this library and want to see your project here please create a pull r MIT License -MQTTnet Copyright (c) 2016-2020 Christian Kratky +MQTTnet Copyright (c) 2016-2021 Christian Kratky Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From a13874d8a58db3b3b710272c8883c0deb1004ce5 Mon Sep 17 00:00:00 2001 From: Christian Date: Sat, 30 Jan 2021 13:17:03 +0100 Subject: [PATCH 15/29] Add missing overloads for client disconnect. Add timeout for disconnect in managed client. --- .../ManagedMqttClient.cs | 15 ++++++++---- .../MqttClientDisconnectOptions.cs | 2 +- Source/MQTTnet/Client/MqttClientExtensions.cs | 23 ++++++++++++------- .../MqttNetScopedLoggerExtensions.cs | 5 ++++ 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClient.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClient.cs index 23105748..931326bc 100644 --- a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClient.cs +++ b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClient.cs @@ -53,11 +53,11 @@ namespace MQTTnet.Extensions.ManagedClient public bool IsConnected => InternalClient.IsConnected; public bool IsStarted => _connectionCancellationToken != null; - + public IMqttClient InternalClient { get; } public int PendingApplicationMessagesCount => _messageQueue.Count; - + public IManagedMqttClientOptions Options { get; private set; } public IMqttClientConnectedHandler ConnectedHandler @@ -92,7 +92,7 @@ namespace MQTTnet.Extensions.ManagedClient if (options == null) throw new ArgumentNullException(nameof(options)); if (options.ClientOptions == null) throw new ArgumentException("The client options are not set.", nameof(options)); - + if (!_maintainConnectionTask?.IsCompleted ?? false) throw new InvalidOperationException("The managed client is already started."); Options = options; @@ -292,7 +292,14 @@ namespace MQTTnet.Extensions.ManagedClient { try { - await InternalClient.DisconnectAsync().ConfigureAwait(false); + using (var disconnectTimeout = new CancellationTokenSource(Options.ClientOptions.CommunicationTimeout)) + { + await InternalClient.DisconnectAsync(disconnectTimeout.Token).ConfigureAwait(false); + } + } + catch (OperationCanceledException) + { + _logger.Warning("Timeout while sending DISCONNECT packet."); } catch (Exception exception) { diff --git a/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectOptions.cs b/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectOptions.cs index 995806e7..f9fdf288 100644 --- a/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectOptions.cs +++ b/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectOptions.cs @@ -1,6 +1,6 @@ namespace MQTTnet.Client.Disconnecting { - public class MqttClientDisconnectOptions + public sealed class MqttClientDisconnectOptions { public MqttClientDisconnectReason ReasonCode { get; set; } = MqttClientDisconnectReason.NormalDisconnection; diff --git a/Source/MQTTnet/Client/MqttClientExtensions.cs b/Source/MQTTnet/Client/MqttClientExtensions.cs index 2408a14a..e9fd4361 100644 --- a/Source/MQTTnet/Client/MqttClientExtensions.cs +++ b/Source/MQTTnet/Client/MqttClientExtensions.cs @@ -128,7 +128,21 @@ namespace MQTTnet.Client { if (client == null) throw new ArgumentNullException(nameof(client)); - return client.DisconnectAsync(new MqttClientDisconnectOptions()); + return client.DisconnectAsync(CancellationToken.None); + } + + public static Task DisconnectAsync(this IMqttClient client, MqttClientDisconnectOptions options) + { + if (client == null) throw new ArgumentNullException(nameof(client)); + + return client.DisconnectAsync(options, CancellationToken.None); + } + + public static Task DisconnectAsync(this IMqttClient client, CancellationToken cancellationToken) + { + if (client == null) throw new ArgumentNullException(nameof(client)); + + return client.DisconnectAsync(new MqttClientDisconnectOptions(), cancellationToken); } public static Task SubscribeAsync(this IMqttClient client, params MqttTopicFilter[] topicFilters) @@ -176,13 +190,6 @@ namespace MQTTnet.Client return client.ConnectAsync(options, CancellationToken.None); } - public static Task DisconnectAsync(this IMqttClient client, MqttClientDisconnectOptions options) - { - if (client == null) throw new ArgumentNullException(nameof(client)); - - return client.DisconnectAsync(options, CancellationToken.None); - } - public static Task SendExtendedAuthenticationExchangeDataAsync(this IMqttClient client, MqttExtendedAuthenticationExchangeData data) { if (client == null) throw new ArgumentNullException(nameof(client)); diff --git a/Source/MQTTnet/Diagnostics/MqttNetScopedLoggerExtensions.cs b/Source/MQTTnet/Diagnostics/MqttNetScopedLoggerExtensions.cs index abb019fa..8ecc94c1 100644 --- a/Source/MQTTnet/Diagnostics/MqttNetScopedLoggerExtensions.cs +++ b/Source/MQTTnet/Diagnostics/MqttNetScopedLoggerExtensions.cs @@ -18,6 +18,11 @@ namespace MQTTnet.Diagnostics { logger.Publish(MqttNetLogLevel.Warning, message, parameters, exception); } + + public static void Warning(this IMqttNetScopedLogger logger, string message, params object[] parameters) + { + logger.Publish(MqttNetLogLevel.Warning, message, parameters, null); + } public static void Error(this IMqttNetScopedLogger logger, Exception exception, string message, params object[] parameters) { From 3f92f2ca2b70627bd92c48cc73640f91fa10cd16 Mon Sep 17 00:00:00 2001 From: Christian Date: Sat, 30 Jan 2021 13:24:24 +0100 Subject: [PATCH 16/29] Remove UWP test app because it gets replaced by MQTTnet.App in a separate repository. --- MQTTnet.sln | 25 - .../MQTTnet.TestApp.UniversalWindows/App.xaml | 6 - .../App.xaml.cs | 51 -- .../Assets/LockScreenLogo.scale-200.png | Bin 1430 -> 0 bytes .../Assets/SplashScreen.scale-200.png | Bin 7700 -> 0 bytes .../Assets/Square150x150Logo.scale-200.png | Bin 2937 -> 0 bytes .../Assets/Square44x44Logo.scale-200.png | Bin 1647 -> 0 bytes ...x44Logo.targetsize-24_altform-unplated.png | Bin 1255 -> 0 bytes .../Assets/StoreLogo.png | Bin 1451 -> 0 bytes .../Assets/Wide310x150Logo.scale-200.png | Bin 3204 -> 0 bytes .../JsonServerStorage.cs | 49 - .../MQTTnet.TestApp.UniversalWindows.csproj | 196 ---- ....TestApp.UniversalWindows_TemporaryKey.pfx | Bin 2504 -> 0 bytes .../MainPage.xaml | 266 ------ .../MainPage.xaml.cs | 834 ------------------ .../Package.appxmanifest | 30 - .../Properties/AssemblyInfo.cs | 14 - .../Properties/Default.rd.xml | 31 - 18 files changed, 1502 deletions(-) delete mode 100644 Tests/MQTTnet.TestApp.UniversalWindows/App.xaml delete mode 100644 Tests/MQTTnet.TestApp.UniversalWindows/App.xaml.cs delete mode 100644 Tests/MQTTnet.TestApp.UniversalWindows/Assets/LockScreenLogo.scale-200.png delete mode 100644 Tests/MQTTnet.TestApp.UniversalWindows/Assets/SplashScreen.scale-200.png delete mode 100644 Tests/MQTTnet.TestApp.UniversalWindows/Assets/Square150x150Logo.scale-200.png delete mode 100644 Tests/MQTTnet.TestApp.UniversalWindows/Assets/Square44x44Logo.scale-200.png delete mode 100644 Tests/MQTTnet.TestApp.UniversalWindows/Assets/Square44x44Logo.targetsize-24_altform-unplated.png delete mode 100644 Tests/MQTTnet.TestApp.UniversalWindows/Assets/StoreLogo.png delete mode 100644 Tests/MQTTnet.TestApp.UniversalWindows/Assets/Wide310x150Logo.scale-200.png delete mode 100644 Tests/MQTTnet.TestApp.UniversalWindows/JsonServerStorage.cs delete mode 100644 Tests/MQTTnet.TestApp.UniversalWindows/MQTTnet.TestApp.UniversalWindows.csproj delete mode 100644 Tests/MQTTnet.TestApp.UniversalWindows/MQTTnet.TestApp.UniversalWindows_TemporaryKey.pfx delete mode 100644 Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml delete mode 100644 Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml.cs delete mode 100644 Tests/MQTTnet.TestApp.UniversalWindows/Package.appxmanifest delete mode 100644 Tests/MQTTnet.TestApp.UniversalWindows/Properties/AssemblyInfo.cs delete mode 100644 Tests/MQTTnet.TestApp.UniversalWindows/Properties/Default.rd.xml diff --git a/MQTTnet.sln b/MQTTnet.sln index 74f27256..505290e3 100644 --- a/MQTTnet.sln +++ b/MQTTnet.sln @@ -9,8 +9,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{32A630A7-2598-41D7-B625-204CD906F5FB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.TestApp.UniversalWindows", "Tests\MQTTnet.TestApp.UniversalWindows\MQTTnet.TestApp.UniversalWindows.csproj", "{FF1F72D6-9524-4422-9497-3CC0002216ED}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "Source\MQTTnet\MQTTnet.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{67C28AC1-BC3A-420A-BE9C-FA2401431CF9}" @@ -91,28 +89,6 @@ Global {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.Build.0 = Release|Any CPU {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.ActiveCfg = Release|Any CPU {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.Build.0 = Release|Any CPU - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.ActiveCfg = Debug|ARM - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.Build.0 = Debug|ARM - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.Deploy.0 = Debug|ARM - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x64.ActiveCfg = Debug|x64 - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x64.Build.0 = Debug|x64 - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x64.Deploy.0 = Debug|x64 - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x86.ActiveCfg = Debug|x86 - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x86.Build.0 = Debug|x86 - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x86.Deploy.0 = Debug|x86 - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|Any CPU.Build.0 = Release|Any CPU - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|ARM.ActiveCfg = Release|ARM - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|ARM.Build.0 = Release|ARM - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|ARM.Deploy.0 = Release|ARM - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x64.ActiveCfg = Release|x64 - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x64.Build.0 = Release|x64 - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x64.Deploy.0 = Release|x64 - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x86.ActiveCfg = Release|x86 - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x86.Build.0 = Release|x86 - {FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x86.Deploy.0 = Release|x86 {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.Build.0 = Debug|Any CPU {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|ARM.ActiveCfg = Debug|Any CPU @@ -327,7 +303,6 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4} - {FF1F72D6-9524-4422-9497-3CC0002216ED} = {A56E3128-1639-4F31-873A-325E14BB6295} {3587E506-55A2-4EB3-99C7-DC01E42D25D2} = {32A630A7-2598-41D7-B625-204CD906F5FB} {3D283AAD-AAA8-4339-8394-52F80B6304DB} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4} {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4} diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/App.xaml b/Tests/MQTTnet.TestApp.UniversalWindows/App.xaml deleted file mode 100644 index bdd5cc0f..00000000 --- a/Tests/MQTTnet.TestApp.UniversalWindows/App.xaml +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/App.xaml.cs b/Tests/MQTTnet.TestApp.UniversalWindows/App.xaml.cs deleted file mode 100644 index 13b5d0b4..00000000 --- a/Tests/MQTTnet.TestApp.UniversalWindows/App.xaml.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using Windows.ApplicationModel; -using Windows.ApplicationModel.Activation; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Navigation; - -namespace MQTTnet.TestApp.UniversalWindows -{ - sealed partial class App - { - public App() - { - InitializeComponent(); - Suspending += OnSuspending; - } - - protected override void OnLaunched(LaunchActivatedEventArgs e) - { - if (!(Window.Current.Content is Frame rootFrame)) - { - rootFrame = new Frame(); - - rootFrame.NavigationFailed += OnNavigationFailed; - - Window.Current.Content = rootFrame; - } - - if (e.PrelaunchActivated == false) - { - if (rootFrame.Content == null) - { - rootFrame.Navigate(typeof(MainPage), e.Arguments); - } - - Window.Current.Activate(); - } - } - - void OnNavigationFailed(object sender, NavigationFailedEventArgs e) - { - throw new Exception("Failed to load Page " + e.SourcePageType.FullName); - } - - private void OnSuspending(object sender, SuspendingEventArgs e) - { - var deferral = e.SuspendingOperation.GetDeferral(); - deferral.Complete(); - } - } -} diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/Assets/LockScreenLogo.scale-200.png b/Tests/MQTTnet.TestApp.UniversalWindows/Assets/LockScreenLogo.scale-200.png deleted file mode 100644 index 735f57adb5dfc01886d137b4e493d7e97cf13af3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1430 zcmaJ>TTC2P7~aKltDttVHYH6u8Io4i*}3fO&d$gd*bA_<3j~&e7%8(eXJLfhS!M@! zKrliY>>6yT4+Kr95$!DoD(Qn-5TP|{V_KS`k~E6(LGS@#`v$hQo&^^BKsw3HIsZBT z_y6C2n`lK@apunKojRQ^(_P}Mgewt$(^BBKCTZ;*xa?J3wQ7~@S0lUvbcLeq1Bg4o zH-bvQi|wt~L7q$~a-gDFP!{&TQfc3fX*6=uHv* zT&1&U(-)L%Xp^djI2?~eBF2cxC@YOP$+9d?P&h?lPy-9M2UT9fg5jKm1t$m#iWE{M zIf%q9@;fyT?0UP>tcw-bLkz;s2LlKl2qeP0w zECS7Ate+Awk|KQ+DOk;fl}Xsy4o^CY=pwq%QAAKKl628_yNPsK>?A>%D8fQG6IgdJ ztnxttBz#NI_a@fk7SU`WtrpsfZsNs9^0(2a z@C3#YO3>k~w7?2hipBf{#b6`}Xw1hlG$yi?;1dDs7k~xDAw@jiI*+tc;t2Lflg&bM)0!Y;0_@=w%`LW^8DsYpS#-bLOklX9r?Ei}TScw|4DbpW%+7 zFgAI)f51s}{y-eWb|vrU-Ya!GuYKP)J7z#*V_k^Xo>4!1Yqj*m)x&0L^tg3GJbVAJ zJ-Pl$R=NAabouV=^z_t;^K*0AvFs!vYU>_<|I^#c?>>CR<(T?=%{;U=aI*SbZADLH z&(f2wz_Y0??Tf|g;?|1Znw6}6U43Q#qNRwv1vp9uFn1)V#*4p&%$mP9x&15^OaBiDS(XppT|z^>;B{PLVEbS3IFYV yGvCsSX*m diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/Assets/SplashScreen.scale-200.png b/Tests/MQTTnet.TestApp.UniversalWindows/Assets/SplashScreen.scale-200.png deleted file mode 100644 index 023e7f1feda78d5100569825acedfd213a0d84e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7700 zcmeHLYj~4Yw%(;oxoEH#Kxq-eR|+VkP17b#Vk;?4QwkI+A{L04G+#<<(x#Un1#+h5>eArRq zTw$)ZvTWW_Y?bDho0nPVTh08+s`sp!j74rJTTtXIDww0SILedFv?sZ?yb@@}GN;#8 znk_b~Q(A0YR#uV4ef!osoV1M3;vQ8N$O|fStfgf$S5;ddUNv`tWtGjM;koG#N;7M< zP*84lnx(bn_KF&9Z5Ai$)#Cs3a|$OFw>WKCT$of*L7_CqQEinflT|W{JT+aKp-E0v zsxmYg)1(T>DROm+LN1eQw8}KCTp=C!$H7`PU!t9_Hw@TsTI2`udRZv*!a5`#A9hK6Y95L(CDUX&_@QxKV z_feX{UhA#ZWlvgpL$#w^D#lq`_A4AzDqd|Zv6y9PX&DNcN|l}_D^{q@GG&H^Pg583 z8FI6N8^H7b5WjGp;urW)d7F+_lcp%KsLX0viCmE(OHH+=%ZfD_=`voUuoUxFO^L;- z;!;2{g-YiiO6m4bs89OuF9!p{FGtH-f%8<2gY!h9s)4ciN%{Kh1+`}{^}M~+TDH9N z^Z5PlgVXMC&2&k*Hw^Lb9gny#ro$MOIxIt{+r)EA10$VR3 zanN8D{TUkl+v0CQ_>ZoHP<M-x#8@8ZiT#$Kh`(uRaX1g$Bg|qy$<#7 zSSAi{Nb8Y=lvNVeio+UGLCAtoLBfL`iOv`)yoJMDJBN>4IH@(l7YRF;61@>qq1iM9 zr@b#OC~SAxSle?5Pp8Z78{VO0YFr1x7kZU64Z23eLf2T2#6J_t;-E}DkB?NufZ0Ug zi?J&byXeaB-uTNVhuiM!UVQw}bZrJ3GtAETYp->!{q#zfN7D3AS9@Q7*V^85jGx#R z(QxYV(wW#F0XF9^^s>>H8pPlVJ>)3Oz z&_X8Sf@~?cH_O*cgi$U#`v`RRfv#y3m(ZpKk^5uLup+lVs$~}FZU$r_+}#hl%?g5m z-u-}-666ssp-xWQak~>PPy$mRc|~?pVSs1_@mBEXpPVfLF6(Ktf1S* zPPh@QZ=tFMs?LM2(5P3L2;l_6XX6s&cYsP1ip#eg0`ZEP0HGYh{UmS@o`MihLLvkU zgyAG0G`b1|qjxxh1(ODKFE%AP}Dq=3vK$P7TXP4GrM1kQ72!GUVMDl`rDC&2;TA}*nF z8$nQD&6ys_nc1*E7$*1S@R8$ymy(sQV}imGSedB@{!QR5P&N_H=-^o!?LsWs+2|mH z-e=)T^SvI)=_JIm7}j4;@*Z17=(#}m=~YF~z~CLI+vdAGlJDcdF$TM?CVI1%LhUrN zaa6DJ=Yh$)$k&Oz{-~8yw^GM^8prYxSxo zvI4k#ibryMa%%*8oI-5m61Koa_A_xg=(fwp0aBX{;X4Q;NXUhtaoJDo1>TqhWtn=_ zd5~chq#&6~c%8JZK#t_&J(9EVUU&upYeIovLt1>vaHe}UUq>#RGQj!EN#5+0@T`(@ z^g~>*c`VGRiSt;!$_4+0hk^I!@O3``5=sZ8IwlxWW7km1B&_t&E*u0_9UBa#VqwY* zz>nxv?FAsVnRaD(Bui=6i==BFUw0k4n$>`umU`F2l?7CYTD^)c2X+d9X&ddS9|gj? zM?knGkGCX&W8offw8aLC2$D{PjC3nVZwd4k?eZH8*mZ)U@3Qk8RDFOz_#WUA#vnzy zyP>KrCfKwSXea7}jgJjBc}PGY+4#6%lbZyjhy`5sZd_Vy6Wz;ixa?czkN}J9It1K6 zY!eu>|AwF^fwZlLAYyQI*lM@^>O>Iu6Vf6i>Q$?v!SeUS<{>UYMwz$*%Aq?w^`j{h z!$GZbhu=^D{&ET8;))LL%ZBDZkQqRd2;u~!d9bHGmLRhLDctNgYyjsuvoSZ#iVdoB z2!f--UUA#U;<{je#?cYt^{PIyKa%hW>}uepWMyAI{{Zo7?2>?$c9;whJae%oN|I-kpTQSx_C$Z&;f zi2i)qmEn=y4U0uvk)$m;zKfjPK@oc?I`}1Jzl$Q~aoKBd3kt7L#7gyt|A_qgz6ai< z=X%D1i!d2h?rHR^R8SUj&G||dkC?DT>{o#Yau<@uqVT{Xef&XG}5*E4aPk{}~ zplx&XhaV)&1EfI3Em;Bw#O5SV^c;{twb-1Rw)+=0!e_BLbd7tYmXCH0wrlOSS+~`7He8Iqx0{CN+DVit9;*6L~JAN zD&cyT)2?h}xnYmL?^)<7YyzZ3$FHU^Eg;DLqAV{#wv#Wj7S`Jdl1pX&{3(uZ?!uh} zDc$ZTNV*7le_W6}Hju~GMTxZQ1aWCeUc%!jv3MHAzt>Y-nQK%zfT*3ebDQA5b?iGn; zBjv3B+GhLTexd_(CzZDP4|#n5^~scvB6#Pk%Ho!kQ>yYw((Dv{6=$g3jT1!u6gORW zx5#`7Wy-ZHRa~IxGHdrp(bm%lf>2%J660nj$fCqN(epv@y!l9s7@k6EvxS{AMP>WY zX4$@F8^kayphIx-RGO$+LYl9YdoI5d|4#q9##`_F5Xnx`&GPzp2fB{-{P@ATw=X@~ z_|&^UMWAKD;jjBKTK(~o?cUFRK8EX=6>cXpfzg4ZpMB>*w_^8GSiT-Jp|xBOnzM+j z*09-@-~qJ(eqWq5@R4i^u4^{McCP(!3}C|v_WsTR*bIUxN(Nx`u##3B4{sE`Z`v8w zAwIG`?1~PkID~W{uDzmqH98Pew_1(;x2%8r^vY{)_&J2K)cN{W+h5+g)ZcjP&Ci#O zgy|8K@4kyMfwilHd&6TDlhb%++Pk!>9HRld6HT7gwyZGrxS$}CsD6`>6!!2K1@Mjf z(P0WYB7V_OFZyeWrbOFb>O54BNXf~K&?}3=^v;v_wT{DKr?jN^DtN&DXwX%u?s*c6`%8>WFz z7}YW^tp0bp^NriE)AB6M2l<7rn7fzePtR*omOevpfm9n?}2V*+0iW;S)C zhg`NAjL?D=W#k*$aR{>pGf~lD-rVtD;5jW1_*Jn1j1=es@Kcx4ySM_bwcQCT=d+DV z>Sz~L=Hj@(X%31nK$mWI@7d>}ORB`K(p=+`UD)+99YUGQc7y^bHZ1F(8|tL0 zdK*DT0kSXG_{BKTpP2*2PecdKV9;dq$^ZZDP;Nyq1kp-&GI5eAyZsK!e3V zK@rPy*{(`KIfo+lc878mDKk^V#`VT05}64kBtk%DgwLrOvLMj5-;*GNKv6c6pzMuL z6EP%ob|_0IW}lLRXCP2!9wWhEw3LA7iF#1O1mIZ@Z=6&bz41F;@S_GvYAG-#CW3z{ zP3+6vHhvP&A3$##Vo9$dT^#MoGg^|MDm=Bt1d2RRwSZ<;ZHICpLBv5Xs!D?BH^(9_ z7`H=N&^v|Z-%mP}wNzG{aiFCsRgwzwq!N6obW9+7(R; z(SZ=23`|`>qil!LMGG{_Heq!BD>(Y-zV9wD)}hz25JA37YR%39;kI4y9pgtcUass6 zP24}ZY$vvYeI`zy&)A_X#nY3017ap*0&jx|mVwyGhg3;!keU53a}Uhm3BZI$N$6Se zLWlAmy1S0xKJm4G_U@sN_Tm=`$xWJSEwKU98rZ&)1R^*$$1vA3oG#&*%SMxY_~oGP zP&PFJatFLM-Ps%84IV-+Ow)T{C7cqUAvauy4C z(FRz&?6$Rypj{xO!`y=*J5o4@U8Q-(y5(*=YoKeZ+-1YdljXxkA#B)zo=FeQH#?Le zycNUmEEHWO9a=X^pb#&cOq7-`7UA87#|S22)<7RUtZo|(zibX=w;K3qur9vy#`MNV z6UUcf9ZwEnKCCp+OoBnF@OdbvH)ANXO0o~Pi9l8=x3))}L<#vO0-~O4!~--Ket?d} zJaqsj<@CD1%S2cTW%rOP{Vto%0sGW~1RMa_j^)5nil0Yw- z0EE#bP+l4#P^%PQ+N*oxu1Zq05xZ!bXfYTg>9c{(Iw*lnjR^>kz%lAN^zFce7rppy zY8zA~3GD=A6d*hze&l4D_wA~+O!56)BZTe_rEu}Ezi<4!kG|W#amBZ5{&XS2@6R~H z{9o^y*BkH4$~yX9U&@CgbOzX1bn9xqF|zh$Dh0Y5y*E0e90*$!ObrHY3Ok0`2=O~r zCuke6KrP9KOf?V(YDsM<6pX2nVoN%M$LT^q#FmtaF?1^27F*IcNX~XRB(|hCFvdcc zc)$=S-)acdk$g4?_>jRqxpI6M3vHZk?0c^3=byamYDNf;uB{3NlKW5IhnOS3DNkMV z?tK8?kJ}pmvp%&&eTVOVjHP`q34hN1@!aK}H(K!vI`~gf|Gv+FNEQD5Yd<~yX7k_l h&G-K)@HZb3BABY{)U1?^%I#E6`MGoTtustd{~yM6srvu` diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/Assets/Square150x150Logo.scale-200.png b/Tests/MQTTnet.TestApp.UniversalWindows/Assets/Square150x150Logo.scale-200.png deleted file mode 100644 index af49fec1a5484db1d52a7f9b5ec90a27c7030186..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2937 zcma)84OCO-8BSud5)jwMLRVKgX(S?$n?Ld|vrsm<$CF7)&zTbyy1FE5bU`Q17MRv`9ue$;R(@8kR;#vJ*IM0>cJIAOte!d7oRgdH zd%ySjdB6L9=gX^A6)VzH7p2l@v~3zJAMw|DFy#^)F@@F*`mqUn=Il>l)8_+ab;nOW{%+iPx z+s{Eu|&pIs)Z7{La9~?xKfyl z#43?gjEL15d4WbOZo#SiP%>DB^+BcnJ=7dHEe;r#G=tuw|ka z%q@}##Uh7;tc%L_64m(kHtw74ty%BJMb)_1)#S0j`)F8_1jF7vScpsnH=0V19bO8y zR`0SjIdCUo&=>JwMQF8KHA<{ODHTiQh}0^@5QRmCA?gOH6_H3K^-_sNB^RrdNuK-R zOO*vOrKCVvDwgUck`kF(E7j{I#iiN;b*ZdCt4m@HPA`EuEqGGf4%!K<;(=I=&Vyrw z%TwcWtxa}8mCZ%Cyf&ActJ6_$ox5z6-D!0-dvnRx6t7y3d+h6QYpKWO;8OdnvERo7 zuEf>ih5`wqY)~o@OeVt-wM?Q!>QzdGRj!bz6fzYrfw$hZfAKzr2-M+D+R>}~oT574c;_3zquHcElqKIsryILt3g8n3jcMb+j?i?-L3FpZJ z2WRVBRdDPc+G5aaYg#5hpE+6nQ|(VSoxT3|biF;BUq#==-27Xi=gihDPYP$7?=9cP zYKE$jeQ|3~_L0VG-(F~2ZPyD0=k{J4Q~h(t__{-mz_w8{JDY9{`1ouzz!Vr5!ECdE z6U~O1k8c}24V7~zzXWTV-Pe4)y}wQJS&q%H5`Fo_f_JvIU489aCX$;P`u#!I-=^4ijC2{&9!O&h>mi?9oYD=GC#%)6{GzN6nQYw+Fal50!#x^asjBBR50i`+mho*ttoqV)ubM2KD9S~k7+FR4>{29?6 z{!l6kDdyTN0YJ9LgkPWeXm|gyi@zM3?0@{&pXT12w|78&W-q!RRF)&iLCEZVH<|fR zN0fr2^t8H(>L?>K#>^+jWROLral(Qy-xoBq1U7A&DV||wClb)Otd9?(gZ|8znMF}D zf<1haWz^s0qgecz;RFGt0C-B4g`jNGHsFU+;{<%t65v^sjk^h$lmWn#B0#_)9ij&d z-~lc`A)YYExi^7sBuPM^Y|wA2g*5?`K?#7tzELQYNxGo$UB$4J8RJp1k(8Jj+~hMT zlN~>M@KTTh^--8y3PK_NZ@AC!{PT=CziBzGd+wTJ^@icH!Bd}%)g8V)%K?|c&WTUk zy}qv1C%(fjRoZ4ozC3{O%@5?)XzH35zHns$pgU*Q?fj4v?fp1Qbm+j;3l;9jam9Da zXVcKjPlQ73x78QPu|Ffm6x?`~e3oD=gl=4kYK?={kD5j~QCXU)`HSdduNNENzA*2$ zOm3PzF!lN5e*06-f1Uot67wY#{o-S1!KZ7E=!~7ynnk9_iJR#kFoNbAOT#^2Gd17F zMmvU6>lndZQGd|ax9kUoXXO+$N?|j@6qpsF&_j7YXvwo_C{JpmLw5&#e6k>atv%es z5)7r*Wvv_JkUpT}M!_o!nVlEk1Zbl=a*2hQ*<|%*K1Glj^FcF`6kTzGQ3lz~2tCc@ z&x|tj;aH&1&9HwcJBcT`;{?a+pnej;M1HO(6Z{#J!cZA04hnFl;NXA+&`=7bjW_^o zfC40u3LMG?NdPtwGl>Tq6u}*QG)}-y;)lu-_>ee3kibW(69n0$0Zy!}9rQz%*v1iO zT9_H>99yIrSPYVy6^);rR}7Yo=J_T@hi+qhTZXnVWyf;JDYm5#eYLTxr*?kiNn!+Y zQ+LUkBafNJ#rH#C(?d5^;gw9o#%daEI{mA*LHPIHPU`#|H$hD zwm>0&+kahQ)E#%~k>&5@&#Vg82H?s%71=)(soi@174pi9--2{w{1$}Sz4zGn3Du&x bht0Iza^2ykEt4(epJ78uh5nDlX8(TxzDYwP diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/Assets/Square44x44Logo.scale-200.png b/Tests/MQTTnet.TestApp.UniversalWindows/Assets/Square44x44Logo.scale-200.png deleted file mode 100644 index ce342a2ec8a61291ba76c54604aea7e9d20af11b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1647 zcmaJ?eM}Q)7(e+G1Q(|`V9JhTI2>MkceK4;p;PR&$Pi?ejk3YQ_3o`S&|W_dsOZ8# zWPTt69g`t$ab`0cj-Y0yiBSOqmd)tG7G(}M5aP0_%&9TijB#&)I{zSE^4@#z^FF`l z`8{8`o%wlL(UI|y2!cdsuVamHH~H86F!*-15em4)NqUpCQM5?aoC_eCf@lV4wvF2a zjDQn1JBL69f&@2M3rvzJcfE!eZ8FZUBlFlC5RD)it33{mF9#B82AiyQE%w)`vlwa> zv{<1sm&kSKK$&%2jSFn7$t&P%%6Ue>R=EAnG8N7fqynWG8L3p!4801a;8{+nliO(qd(jNJ_?+9W3#hLIDLoT6~3fx9=`CC-D}-AMrpEO7HK zt3$GicGPc?GmDjy7K2P@La;eu4!$zWCZ`ym{Z$b zu-O6RM&K4JT|BIZB`E-gxqG%FzanI#+2FFmqHqXG7yxWB=w55RGOM)$xMb(>kSNR z2w=1AZi%z=AmG~yea~XaXJR!v7vLn(RUnELfiB1|6D84ICOS}^Zo2AdN}<&*h}G_u z{xZ!(%>tLT3J3<5XhWy-tg+6)0nmUUENLW8TWA{R6bgVd3X;anYFZ^IRis*_P-C-r z;i>%1^eL3UI2-{w8nuFFcs0e~7J{O2k^~Ce%+Ly4U?|=!0LH=t6()xi<^I-rs+9sF z*q{E-CxZbGPeu#a;XJwE;9S1?#R&uns>^0G3p`hEUF*v`M?@h%T%J%RChmD|EVydq zmHWh*_=S%emRC*mhxaVLzT@>Z2SX0u9v*DIJ@WC^kLVdlGV6LpK$KIrlJqc zpJ921)+3JJdTx|<`G&kXpKkjGJv=76R`yYIQ{#c-`%+`#V(7}Q;&@6U8!Td1`d;?N z_9mnI#?AA}4J!r)LN4!E-@H5eXauuB7TOawS>Y|{-P?NNx-lq+z1W-+y(;39P&&LP zL{N80?&=C*qKmdA^moMZRuPcD!B<*mq$ch=0Cnlitw#txRWhb3%TQvPqjkC`F69G4b! ze7z9MZ#+;_#l?H37UqUhDFb^l&s2{oM$3I0o^Q!yx;;V)QmCMo)Tb_ui|mit8MS?U zm##6$sZZ1$@|s%?l@>4Z<*Q}sRBSKMhb4I{e5LdEhsHIHTe8Bod5c>6QtT>$XgUBz z6MK`kO$=jmt@FqggOhJ5j~e@ygRbG;<{Vu)*+nn9aQeo0;$#j;|MS=S$&L?BeV25z xs3B`@=#`5TF{^6(A1rvdY@|-RtQ|iS5{tyX+wH?;n8E)G$kykv-D^wh{{!TZT%7;_ diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/Tests/MQTTnet.TestApp.UniversalWindows/Assets/Square44x44Logo.targetsize-24_altform-unplated.png deleted file mode 100644 index f6c02ce97e0a802b85f6021e822c89f8bf57d5cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1255 zcmaJ>TWs4@7*5+{G#S+&C!qC#> zf>5N3P6jO*Cz>ug*(_DmW=)kea&m$gZ^+nyiF`;j%w@}y8)>p*SH}C`m?DXeieF2U zyQHecc_L%Gh!7GMt+hG06y;+|p4>m~}PjA}rKViGiEnn7G0ZO<>G|7q;2?NwGCM3s?eued6%hd$B+ z*kQJ{#~$S=DFE(%=E+UkmlEI*%3llUf~8Ja9YU1Vui0IbGBkW_gHB%Rd&!!ioX zs40O?i9I{};kle7GMvE7(rk`la=gTI)47=>%?q@^iL-nUo3}h4S}N-KHn8t5mVP8w z&bSErwp+37 zNJJ8?a|{r5Q3R0Z5s-LB1WHOwYC@7pCHWND#cL1cZ?{kJ368_*(UDWUDyb<}0y@o# zfMF016iMWPCb6obAxT$JlB6(2DrlXDTB&!0`!m??4F(qWMhjVZo?JXQmz`1*58Z=& zcDmB|S-E@j?BoFGix0flckqdS4jsPNzhfWyWIM98GxcLs89C(~dw%$_t;JjX-SD}E zfiGV;{8Q%8r}w9x>EEigW81>`kvnU@pK)4+xk9@+bNj9L!AAZ@SZ@q|)&BmY3+HZx zul~BeG4|}-;L%cHViQGQX?^zFfO0&#cHwel=d`lH9sJ-@Sl@n*(8J2>%Ac`IxyY?Q z{=GhWvC#gu-~Ia7*n{=+;qM?Ul_wy1+u7ho;=`>EwP^g~R@{unBds`!#@}tluZQpS zm)M~nYEifJWJGx?_6DcTy>#uh%>!H9=hb^(v`=m3F1{L>db=<5_tm+_&knAQ2EU$s Mu9UqpbNZeC0BbUo^Z)<= diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/Assets/StoreLogo.png b/Tests/MQTTnet.TestApp.UniversalWindows/Assets/StoreLogo.png deleted file mode 100644 index 7385b56c0e4d3c6b0efe3324aa1194157d837826..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1451 zcmaJ>eN5D57_Z|bH;{0+1#mbl)eTU3{h)Wf7EZV?;HD@XL@{B`Ui%(2aMxQ~xdXSv z5nzWi(LW)U2=Vc-cY@s7nPt{i0hc6!7xN4NNHI#EQl>YNBy8l4%x9gr_W-j zEZMQmmTIy(>;lblRfh`dIyTgc9W5d!VP$L4(kKrN1c5G~(O_#xG zAJCNTstD^5SeXFB+&$h=ToJP2H>xr$iqPs-#O*;4(!Fjw25-!gEb*)mU}=)J;Iu>w zxK(5XoD0wrPSKQ~rbL^Cw6O_03*l*}i=ydbu7adJ6y;%@tjFeXIXT+ms30pmbOP%Q zX}S;+LBh8Tea~TSkHzvX6$rYb)+n&{kSbIqh|c7hmlxmwSiq5iVhU#iEQ<>a18|O^Sln-8t&+t`*{qBWo5M?wFM(JuimAOb5!K#D}XbslM@#1ZVz_;!9U zpfEpLAOz=0g@bd6Xj_ILi-x^!M}73h^o@}hM$1jflTs|Yuj9AL@A3<-?MV4!^4q`e z)fO@A;{9K^?W?DbnesnPr6kK>$zaKo&;FhFd(GYFCIU^T+OIMb%Tqo+P%oq(IdX7S zf6+HLO?7o0m+p>~Tp5UrXWh!UH!wZ5kv!E`_w)PTpI(#Iw{AS`gH4^b(bm^ZCq^FZ zY9DD7bH}rq9mg88+KgA$Zp!iWncuU2n1AuIa@=sWvUR-s`Qb{R*kk(SPU^`$6BXz8 zn#7yaFOIK%qGxyi`dYtm#&qqox0$h=pNi#u=M8zUG@bpiZ=3sT=1}Trr}39cC)H|v zbL?W)=&s4zrh)7>L(|cc%$1#!zfL?HjpeP%T+x_a+jZ16b^iKOHxFEX$7d|8${H-* zIrOJ5w&i$>*D>AKaIoYg`;{L@jM((Kt?$N$5OnuPqVvq**Nm}(f0wwOF%iX_Pba;V z;m@wxX&NcV3?<1+u?A{y_DIj7#m3Af1rCE)o`D&Y3}0%7E;iX1yMDiS)sh0wKi!36 zL!Wmq?P^Ku&rK~HJd97KkLTRl>ScGFYZNlYytWnhmuu|)L&ND8_PmkayQb{HOY640 bno1(wj@u8DCVuFR|31B*4ek@pZJqxCDDe1x diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/Assets/Wide310x150Logo.scale-200.png b/Tests/MQTTnet.TestApp.UniversalWindows/Assets/Wide310x150Logo.scale-200.png deleted file mode 100644 index 288995b397fdbef1fb7e85afd71445d5de1952c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3204 zcmbVPeQXow8NYmBd90>}0NP?GhXW~VaeThm=a0tV#EwJMI!)6M3}|c4_Bl3=Kd>G0 z(GHx1wl<7(tP?FsOQkTilSo*iIvF%uArExJ73~P zSv1xEy!U(Wd4A9D`FQV@W3@F^qJ@PEF$@z`Z!*BbFsS(^?B zyiAzJ+q})bkgiQHWqEb*jJD-coHYr1^iocg)l!Qa{Xqs-l~6J}p-|##ZHYofskQ3$ zI0;xzXyhazBeXhIsg5A=%ufo@f)1yy&ScKS0;HF^!r_2UE^lpZEom(+@duma3awTv zCrCL-%D_SvYWIcdHkmI}#50(fkUi)Qgx!80ju>g1za^}ff>JI8Z@^-iCiaCgg@TgF z+vtE?Q9{VQUX&MW9SYYmGcxA14%N2@7FwBTD4N<(2{nWgV8$e3?-F=L^&FrtWn~(U_Q~~^uYiyeY6-KoTnfh9AWz@ zIKje0)u!_Lw)E}G!#kEfwKVdNt(UAf9*f>tEL_(=xco-T%jTi@7YlC3hs2ik%Le0H ztj}RTeCF(5mwvi3_56>-yB?l;J>-1%!9~=fs|QcNG3J~a@JCu`4SB460s0ZO+##4fFUSGLcj_ja^fL4&BKALfb#$6$O?>P@qx2Agl^x0i&ugt zsy5Pyu=()`7HRMG3IB7F1@`_ z+-!J%#i6e^U$e#+C%Q>_qVRzWRsG^W_n+@OcX@vzI&z;mzHNb!GQ?LWA(wtpqHqTM z1OFw_{Zn?fD)p)`c`kOgv{de=v@suGRqY{N^U7gI1VF3*F=obwaXI6ob5__Yn zVTguS!%(NI09J8x#AO_aW!9W7k*UvB;IWDFC3srwftr{kHj%g)fvnAm;&h_dnl~

MY- zf+K}sCe8qU6Ujs`3ua{U0Of$R_gVQBuUA za0v=mu#vIOqiiAZOr&h*$WyOw&k-xr$;G4Ixa!#TJNr>95(h>l%)PUy4p+^SgR(uR zta%k*?ny-+nAr8spEk1fo{J4i!b^Fia`N{_F6@zidA2ZTTrjl#^5Z-2KfB@Cu}l9s z(*|Z2jc?p~vn2f)3y9i*7zJV1L{$?|&q)4oaT;uXi6>1GkRXVTOzAz(RHEmr=eFIi z`}<>-Q?K0GN8!IYxeP1XKXO+jsJbp~o^);Bc;%b7Flpe7;1`Ny@3r7ZR;?R)aJt8C ziNlEC<@3f_lIV4TwV}&e;D!Ee5_|e#g0LUh=5vmYWYm7&2h*M>QPKvGh9-)wfMMW3 z8J9b%1k7dzPzO0_NGQy92BZ^FR6R~6;^6?lqO;-QUP4BY%cG%3vEhbm#>4vIhPBh3 z-+pZGjh$x%Hp{?=FHsMp0&wNPlj00us{&`1ZOZTqs8%4X&xH=UDr*xyBW(Zp&Em94 zf)ZSfn#yg0N)>!1kWdkqJ^S*z0FF5|fj&qcE#Na|%OY0$uO>!&hP+1ywfD_WXk@4J(?MBftK7>$Nvqh@tDuarN%PrTLQ2Uzysx>UV=V zk^RrDSvdQ?0;=hY67EgII-f4`t=+i*yS=Y~!XlqIy_4x&%+OdfbKOFPXS2X5%4R{N z$SQMX^AK6(fA messages) - { - var json = JsonConvert.SerializeObject(messages); - File.WriteAllText(_filename, json); - - return Task.CompletedTask; - } - - public async Task> LoadRetainedMessagesAsync() - { - await Task.CompletedTask; - - if (!File.Exists(_filename)) - { - return new List(); - } - - try - { - var json = File.ReadAllText(_filename); - return JsonConvert.DeserializeObject>(json); - } - catch - { - return new List(); - } - } - - public void Clear() - { - if (File.Exists(_filename)) - { - File.Delete(_filename); - } - } - } -} diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/MQTTnet.TestApp.UniversalWindows.csproj b/Tests/MQTTnet.TestApp.UniversalWindows/MQTTnet.TestApp.UniversalWindows.csproj deleted file mode 100644 index 3c8c989b..00000000 --- a/Tests/MQTTnet.TestApp.UniversalWindows/MQTTnet.TestApp.UniversalWindows.csproj +++ /dev/null @@ -1,196 +0,0 @@ - - - - - Debug - x86 - {FF1F72D6-9524-4422-9497-3CC0002216ED} - AppContainerExe - Properties - MQTTnet.TestApp.UniversalWindows - MQTTnet.TestApp.UniversalWindows - en-US - UAP - 10.0.18362.0 - 10.0.10240.0 - 14 - 512 - {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - true - MQTTnet.TestApp.UniversalWindows_TemporaryKey.pfx - 1B25E1E3A70C4FEF793B882295F383301B861796 - win10;win10-arm;win10-arm-aot;win10-x86;win10-x86-aot;win10-x64;win10-x64-aot - True - - - true - bin\x86\Debug\ - DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP - ;2008 - full - x86 - false - prompt - true - - - bin\x86\Release\ - TRACE;NETFX_CORE;WINDOWS_UWP - true - ;2008 - pdbonly - x86 - false - prompt - true - false - - - true - bin\ARM\Debug\ - DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP - ;2008 - full - ARM - false - prompt - true - - - bin\ARM\Release\ - TRACE;NETFX_CORE;WINDOWS_UWP - true - ;2008 - pdbonly - ARM - false - prompt - true - true - - - true - bin\x64\Debug\ - DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP - ;2008 - full - x64 - false - prompt - true - - - bin\x64\Release\ - TRACE;NETFX_CORE;WINDOWS_UWP - true - ;2008 - pdbonly - x64 - false - prompt - true - true - - - - App.xaml - - - - MainPage.xaml - - - - - - Designer - - - .editorconfig - - - - - - - - - - - - - - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - - - {C400533A-8EBA-4F0B-BF4D-295C3708604B} - MQTTnet.Extensions.ManagedClient - - - {c444e9c8-95fa-430e-9126-274129de16cd} - MQTTnet.Extensions.Rpc - - - {2BD01D53-4CA5-4142-BE8D-313876395E3E} - MQTTnet.Extensions.WebSocket4Net - - - {3587e506-55a2-4eb3-99c7-dc01e42d25d2} - MQTTnet - - - - - 6.2.10 - - - 4.0.0 - - - - 14.0 - - - true - bin\Debug\ - DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS - ;2008 - true - full - x86 - false - prompt - MinimumRecommendedRules.ruleset - true - - - bin\Release\ - TRACE;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS - true - ;2008 - true - pdbonly - x86 - false - prompt - MinimumRecommendedRules.ruleset - true - - - - \ No newline at end of file diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/MQTTnet.TestApp.UniversalWindows_TemporaryKey.pfx b/Tests/MQTTnet.TestApp.UniversalWindows/MQTTnet.TestApp.UniversalWindows_TemporaryKey.pfx deleted file mode 100644 index b8877c01171c4521fccdd4602d8775b3504fa6e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2504 zcmZXUcR1YX7RP^63`0hV8g)XV2BRd{5WTbPC3;zgjIvl|v@Bsf7;Puo7&W>eN+c33 z(IQ$92CW!=&wEff_&YENg2KV0U~tJegSaCE zh#pjkgZo2qa331>MB(U8{^yGBD-=h!O2bREn1?a_r|SwM2waGRUZ8N$6BIv;@xPch zoEgfXU?}IZsL?nH2Gf6p;-FJG=cf`qIZJUZk~1Wg-wBl!98!)?st`v0LKj1Ob++t; zn>?2`p%hHHhbbtj%pOMF=YbE(h3xAa46a~)eeqIxZhASafz8rzr(dG_Is&m-7o5*V7(p|Le&h^vip{?bp^J` zYwJBU%tsm9f<4Q%5*sHx&eS{1d_uK0Y;(5w;&#@@TZW%Q9&7i1snO=s6mT7MuAjmo zK|Ile54fK$862d>yM#nj=I1U#|CJD{mKEk-Yh`X@H50SW(LBChsc3MRQ@2){b6dD9 zOQU*RiiJ2F!Hf+ek)l~e8XvNc>YsY@Ib=*R1-M_a=n3B442cT6`DyvL&Z%QQqDT$% zd@_l(Hn#hhgh|tCR#r!)>KZ%wz*LO#dPPCpHpQ{7IqkYb%LjbLjkU?m5bmvTrcE_5 zMw!gKYgIOgO19F6%ZT;pKQRQ2pjUxjWeav&vdDD*3t}l`cz|(XQ1L5~l#Cuen!^}+ zXQ`JB4Vl!Yh5K!I&FkaCY@ z)=tUOi1OPxtD5o22@h-wCN&pUU)<{PUCA)9IgzYzOY|;kI~N)cC9nUhLd#vD>uSCR zk9P-M4R7NE^AnvPS&~ODlVTg!{B#u5iC;QDCViXb2}Eag+;yC7%OXz8cKS>!2n3V)NCR4_SDc$V(343&$gJKS-f8Bu3L9BL`$X-%+{cG= zPgRd#_fozt{E;5@!yblX^$RafcV2sB)vs+oh$3lfl>CsUJp{VjF*$KP!=e6bHv3bes-c#UH0didBg*w|h08mgmOj*^srG zpT;Q>tKX291PhT@^=Bl@G+V+)s&W_O8pw?ap}F5I;$t-jD+ybN9PdCycy~@M{OD;9 zp>Na;oRzu!-Ukz*Hvos_6mLo;`JeaSh+Gr>`POH=NS*}gTv(dZhr2hJB`if`H4YEG zu{5sUiVhF}Pq@d|Nv5}P(fP3Wn*Q=j`VOmH?)x391I?Apk~dk8)vYctI%obAEZo)i z@()>$-y&h%5=EYy_jPhSma7sipAXerXWj7(Yq5V}dwJor$l1g9Mn0t7rLV_ek=p{n zV~EcO*js`cWlkaf$B9}B{54^hDT?Ktk{^fWCD>(*KZ}t*1Sy!0-?vb1P)Beb#^TA@ z>D0OMDNiFzfr7AH^bm^u?{x|1l!HN`AOHYR*8e~bIU_*>;0|ybkON!*G~fov(`soz z8E^$uXtgw;NMo*lF%_Cei5Ai{tw`(n?o^^Vl>h}=b^+W_jxe|s3pzq*is`CqoZgu=JX;aAF`e%7j0b^pqlytt#_@mTN#M|Aa;snPrP9gY>h9I;3TajblvHYDZ z_s`T`t2@Qvhh$I^$AW4rSqCZDZRdX=~DWNB>)1Tq_RZTYM?d-15{ z(0K}y3(;E>pD-b8;x$dbzmieNuJM`}U*cPk;LJUhoSdWHZ`U5_&REH_Q}UdxBhFswb-gqHRKpTvlF;wIx5hekVtcyR zA23{8(bOuu+N0WiuSwDBnMNH<{917KF2ip(H}rsNse&q`NcV!Jq+>Ya@?&quUXSQs~pyJtJuR3e3ZGDG*rqbd@Cu-FkS6bdfuM%0KW`bq2H(&T^3!XX1>#VNBy14RW z(4c6nQ`!>@ggJaAW;dht17Y@w1N=XUtNELV)Z(^s3ZtfdFF@Gy6SiV!S@pFvld~6P>#meImra{ Vgz{zM?9=5#7o`j*p`q^|?BD6|gW>=H diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml b/Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml deleted file mode 100644 index 11e68c65..00000000 --- a/Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml +++ /dev/null @@ -1,266 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Server: - - - Port: - - - Username: - - - Password: - - - Client ID: - - - Keep Alive interval: - - - Transport: - - TCP - WS - WS (WebSocket4Net) - Use TLS - - - Protocol: - - 5.0.0 - 3.1.1 - 3.1.0 - - - - Clean session - Use managed client - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Topic: - - - Content type: - - - Payload: - - - Payload format: - - Plain text - Base64 string - - - QoS level: - - 0 (At most once) - 1 (At least once) - 2 (Exactly once) - - - Response topic: - - - - Retain - - - - - - - - Method: - - - Payload: - - - Text - Base64 - - - QoS: - - 0 (At most once) - 1 (At least once) - 2 (Exactly once) - - - Responses: - - - - - - - - - - - - - - - - - - Topic: - - - QoS: - - 0 (At most once) - 1 (At least once) - 2 (Exactly once) - - - Received messages: - Add received messages to list - - - - - - - - - - - - - - - - - - - Port: - - - Persist retained messages in JSON format - Clear previously retained messages on startup - Allow persistent sessions - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml.cs b/Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml.cs deleted file mode 100644 index 836e01f6..00000000 --- a/Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml.cs +++ /dev/null @@ -1,834 +0,0 @@ -using MQTTnet.Client; -using MQTTnet.Client.Connecting; -using MQTTnet.Client.Disconnecting; -using MQTTnet.Client.Options; -using MQTTnet.Diagnostics; -using MQTTnet.Exceptions; -using MQTTnet.Extensions.ManagedClient; -using MQTTnet.Extensions.Rpc; -using MQTTnet.Extensions.Rpc.Options; -using MQTTnet.Extensions.WebSocket4Net; -using MQTTnet.Formatter; -using MQTTnet.Implementations; -using MQTTnet.Protocol; -using MQTTnet.Server; -using MQTTnet.Server.Status; -using System; -using System.Collections.Concurrent; -using System.Collections.ObjectModel; -using System.Text; -using System.Threading.Tasks; -using Windows.Security.Cryptography.Certificates; -using Windows.UI.Core; -using Windows.UI.Xaml; - -namespace MQTTnet.TestApp.UniversalWindows -{ - public sealed partial class MainPage - { - private readonly ConcurrentQueue _traceMessages = new ConcurrentQueue(); - private readonly ObservableCollection _sessions = new ObservableCollection(); - - private IMqttClient _mqttClient; - private IManagedMqttClient _managedMqttClient; - private IMqttServer _mqttServer; - - public MainPage() - { - InitializeComponent(); - - ClientId.Text = Guid.NewGuid().ToString("D"); - - MqttNetGlobalLogger.LogMessagePublished += OnTraceMessagePublished; - } - - private async void OnTraceMessagePublished(object sender, MqttNetLogMessagePublishedEventArgs e) - { - _traceMessages.Enqueue(e.LogMessage); - await UpdateLogAsync(); - } - - private async Task UpdateLogAsync() - { - while (_traceMessages.Count > 100) - { - _traceMessages.TryDequeue(out _); - } - - var logText = new StringBuilder(); - foreach (var traceMessage in _traceMessages) - { - logText.AppendFormat( - "[{0:yyyy-MM-dd HH:mm:ss.fff}] [{1}] [{2}] [{3}] [{4}]{5}", - traceMessage.Timestamp, - traceMessage.Level, - traceMessage.Source, - traceMessage.ThreadId, - traceMessage.Message, - Environment.NewLine); - - if (traceMessage.Exception != null) - { - logText.AppendLine(traceMessage.Exception.ToString()); - } - } - - await Trace.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () => - { - Trace.Text = logText.ToString(); - }); - } - - private async void Connect(object sender, RoutedEventArgs e) - { - var mqttFactory = new MqttFactory(); - - var tlsOptions = new MqttClientTlsOptions - { - UseTls = UseTls.IsChecked == true, - IgnoreCertificateChainErrors = true, - IgnoreCertificateRevocationErrors = true, - AllowUntrustedCertificates = true - }; - - var options = new MqttClientOptions - { - ClientId = ClientId.Text, - ProtocolVersion = MqttProtocolVersion.V500 - }; - - if (UseTcp.IsChecked == true) - { - options.ChannelOptions = new MqttClientTcpOptions - { - Server = Server.Text, - Port = int.Parse(Port.Text), - TlsOptions = tlsOptions - }; - } - - if (UseWs.IsChecked == true) - { - options.ChannelOptions = new MqttClientWebSocketOptions - { - Uri = Server.Text, - TlsOptions = tlsOptions - }; - } - - if (UseWs4Net.IsChecked == true) - { - options.ChannelOptions = new MqttClientWebSocketOptions - { - Uri = Server.Text, - TlsOptions = tlsOptions - }; - - mqttFactory.UseWebSocket4Net(); - } - - if (options.ChannelOptions == null) - { - throw new InvalidOperationException(); - } - - if (!string.IsNullOrEmpty(User.Text)) - { - options.Credentials = new MqttClientCredentials - { - Username = User.Text, - Password = Encoding.UTF8.GetBytes(Password.Text) - }; - } - - options.CleanSession = CleanSession.IsChecked == true; - options.KeepAlivePeriod = TimeSpan.FromSeconds(double.Parse(KeepAliveInterval.Text)); - - if (UseMqtt310.IsChecked == true) - { - options.ProtocolVersion = MqttProtocolVersion.V310; - } - else if (UseMqtt311.IsChecked == true) - { - options.ProtocolVersion = MqttProtocolVersion.V311; - } - else if (UseMqtt500.IsChecked == true) - { - options.ProtocolVersion = MqttProtocolVersion.V500; - } - - try - { - if (_mqttClient != null) - { - await _mqttClient.DisconnectAsync(); - _mqttClient.UseApplicationMessageReceivedHandler(HandleReceivedApplicationMessage); - _mqttClient.ConnectedHandler = new MqttClientConnectedHandlerDelegate(x => OnConnected()); - _mqttClient.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(x => OnDisconnected()); - } - - if (UseManagedClient.IsChecked == true) - { - _managedMqttClient = mqttFactory.CreateManagedMqttClient(); - _managedMqttClient.UseApplicationMessageReceivedHandler(HandleReceivedApplicationMessage); - _managedMqttClient.ConnectedHandler = new MqttClientConnectedHandlerDelegate(x => OnConnected()); - _managedMqttClient.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(x => OnDisconnected()); - - await _managedMqttClient.StartAsync(new ManagedMqttClientOptions - { - ClientOptions = options - }); - } - else - { - _mqttClient = mqttFactory.CreateMqttClient(); - _mqttClient.UseApplicationMessageReceivedHandler(HandleReceivedApplicationMessage); - _mqttClient.ConnectedHandler = new MqttClientConnectedHandlerDelegate(x => OnConnected()); - _mqttClient.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(x => OnDisconnected()); - - await _mqttClient.ConnectAsync(options); - } - } - catch (Exception exception) - { - Trace.Text += exception + Environment.NewLine; - } - } - - private void OnDisconnected() - { - _traceMessages.Enqueue(new MqttNetLogMessage - { - Timestamp = DateTime.UtcNow, - ThreadId = -1, - Level = MqttNetLogLevel.Info, - Message = "! DISCONNECTED EVENT FIRED", - }); - - Task.Run(UpdateLogAsync); - } - - private void OnConnected() - { - _traceMessages.Enqueue(new MqttNetLogMessage - { - Timestamp = DateTime.UtcNow, - ThreadId = -1, - Level = MqttNetLogLevel.Info, - Message = "! CONNECTED EVENT FIRED", - }); - - Task.Run(UpdateLogAsync); - } - - private async Task HandleReceivedApplicationMessage(MqttApplicationMessageReceivedEventArgs eventArgs) - { - var item = $"Timestamp: {DateTime.Now:O} | Topic: {eventArgs.ApplicationMessage.Topic} | Payload: {eventArgs.ApplicationMessage.ConvertPayloadToString()} | QoS: {eventArgs.ApplicationMessage.QualityOfServiceLevel}"; - - await Dispatcher.RunAsync(CoreDispatcherPriority.Low, () => - { - if (AddReceivedMessagesToList.IsChecked == true) - { - ReceivedMessages.Items.Add(item); - } - }); - } - - private async void Publish(object sender, RoutedEventArgs e) - { - try - { - var qos = MqttQualityOfServiceLevel.AtMostOnce; - if (QoS1.IsChecked == true) - { - qos = MqttQualityOfServiceLevel.AtLeastOnce; - } - - if (QoS2.IsChecked == true) - { - qos = MqttQualityOfServiceLevel.ExactlyOnce; - } - - var payload = Array.Empty(); - if (PlainText.IsChecked == true) - { - payload = Encoding.UTF8.GetBytes(Payload.Text); - } - - if (Base64.IsChecked == true) - { - payload = Convert.FromBase64String(Payload.Text); - } - - var message = new MqttApplicationMessageBuilder() - .WithContentType(ContentType.Text) - .WithResponseTopic(ResponseTopic.Text) - .WithTopic(Topic.Text) - .WithPayload(payload) - .WithQualityOfServiceLevel(qos) - .WithRetainFlag(Retain.IsChecked == true) - .Build(); - - if (_mqttClient != null) - { - await _mqttClient.PublishAsync(message); - } - - if (_managedMqttClient != null) - { - await _managedMqttClient.PublishAsync(message); - } - } - catch (Exception exception) - { - Trace.Text += exception + Environment.NewLine; - } - } - - private async void Disconnect(object sender, RoutedEventArgs e) - { - try - { - if (_mqttClient != null) - { - await _mqttClient.DisconnectAsync(); - _mqttClient.Dispose(); - _mqttClient = null; - } - - if (_managedMqttClient != null) - { - await _managedMqttClient.StopAsync(); - _managedMqttClient.Dispose(); - _managedMqttClient = null; - } - } - catch (Exception exception) - { - Trace.Text += exception + Environment.NewLine; - } - } - - private void ClearLog(object sender, RoutedEventArgs e) - { - while (_traceMessages.Count > 0) - { - _traceMessages.TryDequeue(out _); - } - - Trace.Text = string.Empty; - } - - private async void Subscribe(object sender, RoutedEventArgs e) - { - try - { - var qos = MqttQualityOfServiceLevel.AtMostOnce; - if (SubscribeQoS1.IsChecked == true) - { - qos = MqttQualityOfServiceLevel.AtLeastOnce; - } - - if (SubscribeQoS2.IsChecked == true) - { - qos = MqttQualityOfServiceLevel.ExactlyOnce; - } - - var topicFilter = new MqttTopicFilter { Topic = SubscribeTopic.Text, QualityOfServiceLevel = qos }; - - if (_mqttClient != null) - { - await _mqttClient.SubscribeAsync(topicFilter); - } - - if (_managedMqttClient != null) - { - await _managedMqttClient.SubscribeAsync(topicFilter); - } - } - catch (Exception exception) - { - Trace.Text += exception + Environment.NewLine; - } - } - - private async void Unsubscribe(object sender, RoutedEventArgs e) - { - try - { - if (_mqttClient != null) - { - await _mqttClient.UnsubscribeAsync(SubscribeTopic.Text); - } - - if (_managedMqttClient != null) - { - await _managedMqttClient.UnsubscribeAsync(SubscribeTopic.Text); - } - } - catch (Exception exception) - { - Trace.Text += exception + Environment.NewLine; - } - } - - // This code is for the Wiki at GitHub! - // ReSharper disable once UnusedMember.Local - - private async void StartServer(object sender, RoutedEventArgs e) - { - if (_mqttServer != null) - { - return; - } - - JsonServerStorage storage = null; - if (ServerPersistRetainedMessages.IsChecked == true) - { - storage = new JsonServerStorage(); - - if (ServerClearRetainedMessages.IsChecked == true) - { - storage.Clear(); - } - } - - _mqttServer = new MqttFactory().CreateMqttServer(); - - var options = new MqttServerOptions(); - options.DefaultEndpointOptions.Port = int.Parse(ServerPort.Text); - options.Storage = storage; - options.EnablePersistentSessions = ServerAllowPersistentSessions.IsChecked == true; - - await _mqttServer.StartAsync(options); - } - - private async void StopServer(object sender, RoutedEventArgs e) - { - if (_mqttServer == null) - { - return; - } - - await _mqttServer.StopAsync(); - _mqttServer = null; - } - - private void ClearReceivedMessages(object sender, RoutedEventArgs e) - { - ReceivedMessages.Items.Clear(); - } - - private async void ExecuteRpc(object sender, RoutedEventArgs e) - { - var qos = MqttQualityOfServiceLevel.AtMostOnce; - if (RpcQoS1.IsChecked == true) - { - qos = MqttQualityOfServiceLevel.AtLeastOnce; - } - - if (RpcQoS2.IsChecked == true) - { - qos = MqttQualityOfServiceLevel.ExactlyOnce; - } - - var payload = Array.Empty(); - if (RpcText.IsChecked == true) - { - payload = Encoding.UTF8.GetBytes(RpcPayload.Text); - } - - if (RpcBase64.IsChecked == true) - { - payload = Convert.FromBase64String(RpcPayload.Text); - } - - try - { - var rpcClient = new MqttRpcClient(_mqttClient, new MqttRpcClientOptions()); - var response = await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(5), RpcMethod.Text, payload, qos); - - RpcResponses.Items.Add(RpcMethod.Text + " >>> " + Encoding.UTF8.GetString(response)); - } - catch (MqttCommunicationTimedOutException) - { - RpcResponses.Items.Add(RpcMethod.Text + " >>> [TIMEOUT]"); - } - catch (Exception exception) - { - RpcResponses.Items.Add(RpcMethod.Text + " >>> [EXCEPTION (" + exception.Message + ")]"); - } - } - - private void ClearRpcResponses(object sender, RoutedEventArgs e) - { - RpcResponses.Items.Clear(); - } - - private void ClearSessions(object sender, RoutedEventArgs e) - { - _sessions.Clear(); - } - - private void RefreshSessions(object sender, RoutedEventArgs e) - { - if (_mqttServer == null) - { - return; - } - - var sessions = _mqttServer.GetClientStatusAsync().GetAwaiter().GetResult(); - _sessions.Clear(); - - foreach (var session in sessions) - { - _sessions.Add(session); - } - - ListViewSessions.DataContext = _sessions; - } - - #region Wiki Code - -#pragma warning disable IDE0051 // Remove unused private members - private async Task WikiCode() -#pragma warning restore IDE0051 // Remove unused private members - { - { - // Use a custom identifier for the trace messages. - var clientOptions = new MqttClientOptionsBuilder() - .Build(); - } - - { - // Create a new MQTT client. - var factory = new MqttFactory(); - var client = factory.CreateMqttClient(); - - // Create TCP based options using the builder. - var options = new MqttClientOptionsBuilder() - .WithClientId("Client1") - .WithTcpServer("broker.hivemq.com") - .WithCredentials("bud", "%spencer%") - .WithTls() - .WithCleanSession() - .Build(); - - await client.ConnectAsync(options); - - // Reconnecting - - client.UseDisconnectedHandler(async e => - { - Console.WriteLine("### DISCONNECTED FROM SERVER ###"); - await Task.Delay(TimeSpan.FromSeconds(5)); - - try - { - await client.ConnectAsync(options); - } - catch - { - Console.WriteLine("### RECONNECTING FAILED ###"); - } - }); - - // Consuming messages - - client.UseApplicationMessageReceivedHandler(e => - { - Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###"); - Console.WriteLine($"+ Topic = {e.ApplicationMessage.Topic}"); - Console.WriteLine($"+ Payload = {Encoding.UTF8.GetString(e.ApplicationMessage.Payload)}"); - Console.WriteLine($"+ QoS = {e.ApplicationMessage.QualityOfServiceLevel}"); - Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}"); - Console.WriteLine(); - }); - - void Handler(MqttApplicationMessageReceivedEventArgs args) - { - //... - } - - client.UseApplicationMessageReceivedHandler(e => Handler(e)); - - // Subscribe after connect - - client.UseConnectedHandler(async e => - { - Console.WriteLine("### CONNECTED WITH SERVER ###"); - - // Subscribe to a topic - await client.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("my/topic").Build()); - - Console.WriteLine("### SUBSCRIBED ###"); - }); - - // Subscribe to a topic - await client.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("my/topic").Build()); - - // Unsubscribe from a topic - await client.UnsubscribeAsync("my/topic"); - - // Publish an application message - var applicationMessage = new MqttApplicationMessageBuilder() - .WithTopic("A/B/C") - .WithPayload("Hello World") - .WithAtLeastOnceQoS() - .Build(); - - await client.PublishAsync(applicationMessage); - } - - { - - { - // Use TCP connection. - var options = new MqttClientOptionsBuilder() - .WithTcpServer("broker.hivemq.com", 1883) // Port is optional - .Build(); - } - - { - // Use secure TCP connection. - var options = new MqttClientOptionsBuilder() - .WithTcpServer("broker.hivemq.com") - .WithTls() - .Build(); - } - - { - // Use WebSocket connection. - var options = new MqttClientOptionsBuilder() - .WithWebSocketServer("broker.hivemq.com:8000/mqtt") - .Build(); - } - - { - // Create TCP based options manually - var options = new MqttClientOptions - { - ClientId = "Client1", - Credentials = new MqttClientCredentials - { - Username = "bud", - Password = Encoding.UTF8.GetBytes("%spencer%") - }, - ChannelOptions = new MqttClientTcpOptions - { - Server = "broker.hivemq.org", - TlsOptions = new MqttClientTlsOptions - { - UseTls = true - } - }, - }; - } - } - - // ---------------------------------- - { - var options = new MqttServerOptions - { - ConnectionValidator = new MqttServerConnectionValidatorDelegate(c => - { - if (c.ClientId.Length < 10) - { - c.ReasonCode = MqttConnectReasonCode.ClientIdentifierNotValid; - return; - } - - if (c.Username != "mySecretUser") - { - c.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword; - return; - } - - if (c.Password != "mySecretPassword") - { - c.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword; - return; - } - - c.ReasonCode = MqttConnectReasonCode.Success; - }) - }; - - var factory = new MqttFactory(); - var mqttServer = factory.CreateMqttServer(); - await mqttServer.StartAsync(options); - - Console.WriteLine("Press any key to exit."); - Console.ReadLine(); - - await mqttServer.StopAsync(); - } - - // ---------------------------------- - - // For UWP apps: - MqttTcpChannel.CustomIgnorableServerCertificateErrorsResolver = o => - { - if (o.Server == "server_with_revoked_cert") - { - return new[] { ChainValidationResult.Revoked }; - } - - return Array.Empty(); - }; - - { - // Start a MQTT server. - var mqttServer = new MqttFactory().CreateMqttServer(); - await mqttServer.StartAsync(new MqttServerOptions()); - Console.WriteLine("Press any key to exit."); - Console.ReadLine(); - await mqttServer.StopAsync(); - } - - { - // Configure MQTT server. - var optionsBuilder = new MqttServerOptionsBuilder() - .WithConnectionBacklog(100) - .WithDefaultEndpointPort(1884); - - var options = new MqttServerOptions - { - }; - - options.ConnectionValidator = new MqttServerConnectionValidatorDelegate(c => - { - if (c.ClientId != "Highlander") - { - c.ReasonCode = MqttConnectReasonCode.ClientIdentifierNotValid; - return; - } - - c.ReasonCode = MqttConnectReasonCode.Success; - }); - - var mqttServer = new MqttFactory().CreateMqttServer(); - await mqttServer.StartAsync(optionsBuilder.Build()); - } - - { - // Setup client validator. - var options = new MqttServerOptions - { - ConnectionValidator = new MqttServerConnectionValidatorDelegate(c => - { - if (c.ClientId.Length < 10) - { - c.ReasonCode = MqttConnectReasonCode.ClientIdentifierNotValid; - return; - } - - if (c.Username != "mySecretUser") - { - c.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword; - return; - } - - if (c.Password != "mySecretPassword") - { - c.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword; - return; - } - - c.ReasonCode = MqttConnectReasonCode.Success; - }) - }; - } - - { - // Create a new MQTT server. - var mqttServer = new MqttFactory().CreateMqttServer(); - } - - { - // Setup application message interceptor. - var options = new MqttServerOptionsBuilder() - .WithApplicationMessageInterceptor(context => - { - if (context.ApplicationMessage.Topic == "my/custom/topic") - { - context.ApplicationMessage.Payload = Encoding.UTF8.GetBytes("The server injected payload."); - } - - // It is also possible to read the payload and extend it. For example by adding a timestamp in a JSON document. - // This is useful when the IoT device has no own clock and the creation time of the message might be important. - }) - .Build(); - } - - { - // Setup subscription interceptor. - var options = new MqttServerOptionsBuilder() - .WithSubscriptionInterceptor(context => - { - if (context.TopicFilter.Topic.StartsWith("admin/foo/bar") && context.ClientId != "theAdmin") - { - context.AcceptSubscription = false; - } - - if (context.TopicFilter.Topic.StartsWith("the/secret/stuff") && context.ClientId != "Imperator") - { - context.AcceptSubscription = false; - context.CloseConnection = true; - } - }) - .Build(); - } - - { - // Setup and start a managed MQTT client. - var options = new ManagedMqttClientOptionsBuilder() - .WithAutoReconnectDelay(TimeSpan.FromSeconds(5)) - .WithClientOptions(new MqttClientOptionsBuilder() - .WithClientId("Client1") - .WithTcpServer("broker.hivemq.com") - .WithTls().Build()) - .Build(); - - var mqttClient = new MqttFactory().CreateManagedMqttClient(); - await mqttClient.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("my/topic").Build()); - await mqttClient.StartAsync(options); - } - - { - // Use a custom log ID for the logger. - var factory = new MqttFactory(); - var client = factory.CreateMqttClient(new MqttNetLogger("MyCustomId")); - } - - { - var client = new MqttFactory().CreateMqttClient(); - - var message = new MqttApplicationMessageBuilder() - .WithTopic("MyTopic") - .WithPayload("Hello World") - .WithExactlyOnceQoS() - .WithRetainFlag() - .Build(); - - await client.PublishAsync(message); - } - - { - // Write all trace messages to the console window. - MqttNetGlobalLogger.LogMessagePublished += (s, e) => - { - var trace = $">> [{e.LogMessage.Timestamp:O}] [{e.LogMessage.ThreadId}] [{e.LogMessage.Source}] [{e.LogMessage.Level}]: {e.LogMessage.Message}"; - if (e.LogMessage.Exception != null) - { - trace += Environment.NewLine + e.LogMessage.Exception.ToString(); - } - - Console.WriteLine(trace); - }; - } - } - - #endregion - } -} diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/Package.appxmanifest b/Tests/MQTTnet.TestApp.UniversalWindows/Package.appxmanifest deleted file mode 100644 index 81914fad..00000000 --- a/Tests/MQTTnet.TestApp.UniversalWindows/Package.appxmanifest +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - MQTTnet.TestApp.UniversalWindows - test - Assets\StoreLogo.png - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/Properties/AssemblyInfo.cs b/Tests/MQTTnet.TestApp.UniversalWindows/Properties/AssemblyInfo.cs deleted file mode 100644 index d125e02e..00000000 --- a/Tests/MQTTnet.TestApp.UniversalWindows/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("MQTTnet.TestApp.UniversalWindows")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Christian Kratky")] -[assembly: AssemblyProduct("MQTTnet")] -[assembly: AssemblyCopyright("Copyright © Christian Kratky 2016-2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] -[assembly: ComVisible(false)] \ No newline at end of file diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/Properties/Default.rd.xml b/Tests/MQTTnet.TestApp.UniversalWindows/Properties/Default.rd.xml deleted file mode 100644 index 7c40ffeb..00000000 --- a/Tests/MQTTnet.TestApp.UniversalWindows/Properties/Default.rd.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file From 63ca6086d5e95de4930dc9211c597fee77e1542c Mon Sep 17 00:00:00 2001 From: HansM Date: Sat, 30 Jan 2021 15:39:04 +0100 Subject: [PATCH 17/29] Added missing changelog entry. Missing changelog change from https://github.com/chkr1011/MQTTnet/pull/1076. --- Build/MQTTnet.nuspec | 1 + 1 file changed, 1 insertion(+) diff --git a/Build/MQTTnet.nuspec b/Build/MQTTnet.nuspec index b3a4c5ba..42e6d886 100644 --- a/Build/MQTTnet.nuspec +++ b/Build/MQTTnet.nuspec @@ -28,6 +28,7 @@ * [Server] Improved message processing performance (+ ~5%). * [Server] Fix wrong usage of client session items for undelivered messages. * [Server] Allow to respond with a reason code in PUBACK/PUBREC (thanks to @muneebmajeed). +* [Server] Fixed topic filter comparer on edge cases, e.g. "foo/" matching "foo/#" (thanks to @dagophil). Git commit: $gitCommit From 109acccd190bcdb2adb68f26d01e210398f6f35d Mon Sep 17 00:00:00 2001 From: HansM Date: Sun, 31 Jan 2021 11:34:27 +0100 Subject: [PATCH 18/29] Update MQTTnet.nuspec Added code documentation hint. --- Build/MQTTnet.nuspec | 1 + 1 file changed, 1 insertion(+) diff --git a/Build/MQTTnet.nuspec b/Build/MQTTnet.nuspec index 42e6d886..d5435c6d 100644 --- a/Build/MQTTnet.nuspec +++ b/Build/MQTTnet.nuspec @@ -29,6 +29,7 @@ * [Server] Fix wrong usage of client session items for undelivered messages. * [Server] Allow to respond with a reason code in PUBACK/PUBREC (thanks to @muneebmajeed). * [Server] Fixed topic filter comparer on edge cases, e.g. "foo/" matching "foo/#" (thanks to @dagophil). +* [Core] Added code documentation. Git commit: $gitCommit From bb52159e496fc97d334e657c78c77ae3a26621c9 Mon Sep 17 00:00:00 2001 From: HansM Date: Sun, 31 Jan 2021 17:35:39 +0100 Subject: [PATCH 19/29] Added reference to SparkplugNet. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 31e4caad..5576b311 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,7 @@ This library is used in the following projects: * MQTT Tester (MQTT client test app for [Android](https://play.google.com/store/apps/details?id=com.liveowl.mqtttester) and [iOS](https://itunes.apple.com/us/app/mqtt-tester/id1278621826?mt=8)) * MQTTnet App (Cross platform client application for MQTT debugging, inspection etc., ) * Wirehome.Core (Open Source Home Automation system for .NET Core, ) +* SparkplugNet (Sparkplug library for .Net, ) Further projects using this project can be found under https://github.com/chkr1011/MQTTnet/network/dependents. From 57652c76cfb4aeb1af457eea65c93d816504831a Mon Sep 17 00:00:00 2001 From: Volker Milbrandt Date: Thu, 4 Feb 2021 21:09:36 +0100 Subject: [PATCH 20/29] tests with MqttClient.ConnectAsync with credentials for all four combinations of null/non-null for username and password --- Tests/MQTTnet.Core.Tests/Server_Tests.cs | 52 +++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/Tests/MQTTnet.Core.Tests/Server_Tests.cs b/Tests/MQTTnet.Core.Tests/Server_Tests.cs index b2b3b700..68aacdfd 100644 --- a/Tests/MQTTnet.Core.Tests/Server_Tests.cs +++ b/Tests/MQTTnet.Core.Tests/Server_Tests.cs @@ -13,6 +13,7 @@ using MQTTnet.Client.Disconnecting; using MQTTnet.Client.Options; using MQTTnet.Client.Receiving; using MQTTnet.Client.Subscribing; +using MQTTnet.Exceptions; using MQTTnet.Protocol; using MQTTnet.Server; using MQTTnet.Tests.Mockups; @@ -22,6 +23,55 @@ namespace MQTTnet.Tests [TestClass] public class Server_Tests { + [TestMethod] + [DataRow("", null)] + [DataRow("", "")] + [DataRow(null, null)] + public async Task Use_Admissible_Credentials(string username, string password) + { + using (var testEnvironment = new TestEnvironment()) + { + await testEnvironment.StartServerAsync(); + + var client = testEnvironment.CreateClient(); + + var clientOptions = new MqttClientOptionsBuilder() + .WithTcpServer("localhost", testEnvironment.ServerPort) + .WithCredentials(username, password) + .Build(); + + var connectResult = await client.ConnectAsync(clientOptions); + + Assert.IsFalse(connectResult.IsSessionPresent); + Assert.IsTrue(client.IsConnected); + } + } + + [TestMethod] + public async Task Use_Username_Null_Password_Empty() + { + string username = null; + string password = string.Empty; + + using (var testEnvironment = new TestEnvironment()) + { + testEnvironment.IgnoreClientLogErrors = true; + + await testEnvironment.StartServerAsync(); + + var client = testEnvironment.CreateClient(); + + var clientOptions = new MqttClientOptionsBuilder() + .WithTcpServer("localhost", testEnvironment.ServerPort) + .WithCredentials(username, password) + .Build(); + + var ex = await Assert.ThrowsExceptionAsync(async () => await client.ConnectAsync(clientOptions)); + Assert.IsInstanceOfType(ex.InnerException, typeof(MqttProtocolViolationException)); + Assert.AreEqual("If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22].", ex.Message, false); + } + } + [TestMethod] public async Task Use_Empty_Client_ID() { @@ -1063,7 +1113,7 @@ namespace MQTTnet.Tests // forever. This is security related. var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); await client.ConnectAsync("localhost", testEnvironment.ServerPort); - + var buffer = Encoding.UTF8.GetBytes("Garbage"); client.Send(buffer, buffer.Length, SocketFlags.None); From 18cc45484278f0c470874758fb520ad4fcee9822 Mon Sep 17 00:00:00 2001 From: Christian Date: Sat, 6 Feb 2021 13:36:23 +0100 Subject: [PATCH 21/29] Fix build script for .net5.0. --- Build/build.ps1 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Build/build.ps1 b/Build/build.ps1 index 3275bf18..75907bdf 100644 --- a/Build/build.ps1 +++ b/Build/build.ps1 @@ -31,10 +31,12 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M &$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" +&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" # Build the ASP.NET Core 2.0 extension &$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" +&$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" # Build the RPC extension &$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" @@ -43,6 +45,7 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M &$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" +&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" # Build the Managed Client extension &$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" @@ -51,6 +54,7 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M &$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" +&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" # Build the WebSocket4Net extension &$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" @@ -59,6 +63,7 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M &$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" +&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" # Create NuGet packages. From 8ba960343c2a48f4320fcd91f6a775827f6f6208 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 8 Feb 2021 13:29:15 +0100 Subject: [PATCH 22/29] Fix exception handling issues (#1082). --- .../Adapter/MqttConnectingFailedException.cs | 14 +++++----- Source/MQTTnet/Client/MqttClient.cs | 27 ++++++++++++------- Tests/MQTTnet.Core.Tests/Server_Tests.cs | 10 +++---- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/Source/MQTTnet/Adapter/MqttConnectingFailedException.cs b/Source/MQTTnet/Adapter/MqttConnectingFailedException.cs index ab49d934..447aaa69 100644 --- a/Source/MQTTnet/Adapter/MqttConnectingFailedException.cs +++ b/Source/MQTTnet/Adapter/MqttConnectingFailedException.cs @@ -1,17 +1,19 @@ -using MQTTnet.Client.Connecting; +using System; +using MQTTnet.Client.Connecting; using MQTTnet.Exceptions; namespace MQTTnet.Adapter { - public class MqttConnectingFailedException : MqttCommunicationException + public sealed class MqttConnectingFailedException : MqttCommunicationException { - public MqttConnectingFailedException(MqttClientAuthenticateResult result) - : base($"Connecting with MQTT server failed ({result.ResultCode.ToString()}).") + public MqttConnectingFailedException(string message, Exception innerException, MqttClientAuthenticateResult authenticateResult) + : base(message, innerException) { - Result = result; + Result = authenticateResult; } public MqttClientAuthenticateResult Result { get; } - public MqttClientConnectResultCode ResultCode => Result.ResultCode; + + public MqttClientConnectResultCode ResultCode => Result?.ResultCode ?? MqttClientConnectResultCode.UnspecifiedError; } } diff --git a/Source/MQTTnet/Client/MqttClient.cs b/Source/MQTTnet/Client/MqttClient.cs index 6b454d90..3414e990 100644 --- a/Source/MQTTnet/Client/MqttClient.cs +++ b/Source/MQTTnet/Client/MqttClient.cs @@ -280,16 +280,25 @@ namespace MQTTnet.Client async Task AuthenticateAsync(IMqttChannelAdapter channelAdapter, MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken) { - var connectPacket = channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnectPacket( - willApplicationMessage, - Options); + MqttClientAuthenticateResult result; - var connAckPacket = await SendAndReceiveAsync(connectPacket, cancellationToken).ConfigureAwait(false); - var result = channelAdapter.PacketFormatterAdapter.DataConverter.CreateClientConnectResult(connAckPacket); + try + { + var connectPacket = channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnectPacket( + willApplicationMessage, + Options); + + var connAckPacket = await SendAndReceiveAsync(connectPacket, cancellationToken).ConfigureAwait(false); + result = channelAdapter.PacketFormatterAdapter.DataConverter.CreateClientConnectResult(connAckPacket); + } + catch (Exception exception) + { + throw new MqttConnectingFailedException($"Error while authenticating. {exception.Message}", exception, null); + } if (result.ResultCode != MqttClientConnectResultCode.Success) { - throw new MqttConnectingFailedException(result); + throw new MqttConnectingFailedException($"Connecting with MQTT server failed ({result.ResultCode}).", null, result); } _logger.Verbose("Authenticated MQTT connection with server established."); @@ -404,10 +413,10 @@ namespace MQTTnet.Client _sendTracker.Restart(); await _adapter.SendPacketAsync(requestPacket, cancellationToken).ConfigureAwait(false); } - catch (Exception e) + catch (Exception exception) { - _logger.Warning(e, "Error when sending request packet ({0}).", requestPacket.GetType().Name); - packetAwaiter.Cancel(); + _logger.Warning(exception, "Error when sending request packet ({0}).", requestPacket.GetType().Name); + packetAwaiter.Fail(exception); } try diff --git a/Tests/MQTTnet.Core.Tests/Server_Tests.cs b/Tests/MQTTnet.Core.Tests/Server_Tests.cs index a5e6837f..09f085bb 100644 --- a/Tests/MQTTnet.Core.Tests/Server_Tests.cs +++ b/Tests/MQTTnet.Core.Tests/Server_Tests.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using MQTTnet.Adapter; using MQTTnet.Client; using MQTTnet.Client.Connecting; @@ -69,9 +69,9 @@ namespace MQTTnet.Tests .WithCredentials(username, password) .Build(); - var ex = await Assert.ThrowsExceptionAsync(async () => await client.ConnectAsync(clientOptions)); + var ex = await Assert.ThrowsExceptionAsync(async () => await client.ConnectAsync(clientOptions)); Assert.IsInstanceOfType(ex.InnerException, typeof(MqttProtocolViolationException)); - Assert.AreEqual("If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22].", ex.Message, false); + Assert.AreEqual("Error while authenticating. If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22].", ex.Message, false); } } @@ -466,7 +466,7 @@ namespace MQTTnet.Tests public async Task Publish_Multiple_Clients() { var receivedMessagesCount = 0; - + using (var testEnvironment = new TestEnvironment(TestContext)) { await testEnvironment.StartServerAsync(); @@ -496,7 +496,7 @@ namespace MQTTnet.Tests } SpinWait.SpinUntil(() => receivedMessagesCount == 1000, TimeSpan.FromSeconds(20)); - + Assert.AreEqual(1000, receivedMessagesCount); } } From 88cc79d52fb64e7f8af0510b019b6161ad8da95a Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 11 Feb 2021 12:25:58 +0100 Subject: [PATCH 23/29] Fix issues in nuspecs. --- Build/MQTTnet.AspNetCore.nuspec | 2 +- Build/MQTTnet.Extensions.ManagedClient.nuspec | 4 ++-- Build/MQTTnet.Extensions.Rpc.nuspec | 2 +- Build/MQTTnet.Extensions.WebSocket4Net.nuspec | 2 +- Build/MQTTnet.nuspec | 19 +------------------ 5 files changed, 6 insertions(+), 23 deletions(-) diff --git a/Build/MQTTnet.AspNetCore.nuspec b/Build/MQTTnet.AspNetCore.nuspec index c2641907..f9bc8a04 100644 --- a/Build/MQTTnet.AspNetCore.nuspec +++ b/Build/MQTTnet.AspNetCore.nuspec @@ -38,7 +38,7 @@ - + diff --git a/Build/MQTTnet.Extensions.ManagedClient.nuspec b/Build/MQTTnet.Extensions.ManagedClient.nuspec index b3322947..b6587bfd 100644 --- a/Build/MQTTnet.Extensions.ManagedClient.nuspec +++ b/Build/MQTTnet.Extensions.ManagedClient.nuspec @@ -27,7 +27,7 @@ - + @@ -42,7 +42,7 @@ - + diff --git a/Build/MQTTnet.Extensions.Rpc.nuspec b/Build/MQTTnet.Extensions.Rpc.nuspec index 20ab9ef4..05eb8fa4 100644 --- a/Build/MQTTnet.Extensions.Rpc.nuspec +++ b/Build/MQTTnet.Extensions.Rpc.nuspec @@ -27,7 +27,7 @@ - + diff --git a/Build/MQTTnet.Extensions.WebSocket4Net.nuspec b/Build/MQTTnet.Extensions.WebSocket4Net.nuspec index 3def4dab..7d150148 100644 --- a/Build/MQTTnet.Extensions.WebSocket4Net.nuspec +++ b/Build/MQTTnet.Extensions.WebSocket4Net.nuspec @@ -42,7 +42,7 @@ - + diff --git a/Build/MQTTnet.nuspec b/Build/MQTTnet.nuspec index d5435c6d..0e87c3da 100644 --- a/Build/MQTTnet.nuspec +++ b/Build/MQTTnet.nuspec @@ -12,24 +12,7 @@ true MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports v3.1.0, v3.1.1 and v5.0.0 of the MQTT protocol. -* [Core] Added .NET 5.0 TFMs (thanks to @StefanOssendorf, @JanEggers). -* [Core] Added support for TLS 1.3 (requires .NET Core 3.1+) (thanks to @Dvergatal). -* [Core] Added support for _MaximumQoS_ transmit when using MQTTv5 (thanks to @nayato). -* [Core] Aligned the format of some log messages. -* [Client] Fixed an issue with connection state detection (thanks to @tobiasfrick). -* [Client] Fixed an issue when receiving partial QoS 2 packets after a reconnect. -* [Client] Added new API for raw packet inspection. -* [ManagedClient] Removed _AutoReconnect_ since this does no longer work (BREAKING CHANGE!). -* [RpcClient] Moved the RPC topic validation to the topic generation strategy. -* [RcpClient] Added interface for _MqttRpcClient_. -* [Server] Reduced async tasks count by moving dedicated keep alive tasks per connection to shared one. -* [Server] Session takeover and keep alive timeout are now properly set in DISCONNECT packet. -* [Server] Fixed bug in PubRel packet generation (MQTTv5 only). -* [Server] Improved message processing performance (+ ~5%). -* [Server] Fix wrong usage of client session items for undelivered messages. -* [Server] Allow to respond with a reason code in PUBACK/PUBREC (thanks to @muneebmajeed). -* [Server] Fixed topic filter comparer on edge cases, e.g. "foo/" matching "foo/#" (thanks to @dagophil). -* [Core] Added code documentation. +* [Core] Fixed some issues in nuget packages. Git commit: $gitCommit From fda427261179b201dd08953007573a51f0a37c52 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 11 Feb 2021 12:31:13 +0100 Subject: [PATCH 24/29] Update docs. --- Build/MQTTnet.nuspec | 1 + 1 file changed, 1 insertion(+) diff --git a/Build/MQTTnet.nuspec b/Build/MQTTnet.nuspec index 0e87c3da..12e117ff 100644 --- a/Build/MQTTnet.nuspec +++ b/Build/MQTTnet.nuspec @@ -13,6 +13,7 @@ MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports v3.1.0, v3.1.1 and v5.0.0 of the MQTT protocol. * [Core] Fixed some issues in nuget packages. +* [Client] Changed exception types so that proper exceptions are thrown when connecting (#1082). Git commit: $gitCommit From 8b164a484607db3dc97263667c0f94ca5280fefb Mon Sep 17 00:00:00 2001 From: SilverFox Date: Wed, 17 Feb 2021 20:17:54 +0800 Subject: [PATCH 25/29] Update nuspecs Manually sync with the file generated by `msbuild /t:pack` or `dotnet pack`, and remove some unnessary dependencies --- Build/MQTTnet.AspNetCore.nuspec | 8 ++++++ Build/MQTTnet.Extensions.ManagedClient.nuspec | 3 +++ Build/MQTTnet.Extensions.Rpc.nuspec | 3 +++ Build/MQTTnet.Extensions.WebSocket4Net.nuspec | 21 ++++----------- Build/MQTTnet.nuspec | 26 +++++-------------- Build/build.ps1 | 8 +++--- Source/MQTTnet/MQTTnet.csproj | 20 -------------- 7 files changed, 30 insertions(+), 59 deletions(-) diff --git a/Build/MQTTnet.AspNetCore.nuspec b/Build/MQTTnet.AspNetCore.nuspec index f9bc8a04..e8e01d3b 100644 --- a/Build/MQTTnet.AspNetCore.nuspec +++ b/Build/MQTTnet.AspNetCore.nuspec @@ -28,6 +28,14 @@ + + + + + + + + diff --git a/Build/MQTTnet.Extensions.ManagedClient.nuspec b/Build/MQTTnet.Extensions.ManagedClient.nuspec index b6587bfd..b360569a 100644 --- a/Build/MQTTnet.Extensions.ManagedClient.nuspec +++ b/Build/MQTTnet.Extensions.ManagedClient.nuspec @@ -35,6 +35,9 @@ + + + diff --git a/Build/MQTTnet.Extensions.Rpc.nuspec b/Build/MQTTnet.Extensions.Rpc.nuspec index 05eb8fa4..283ac11e 100644 --- a/Build/MQTTnet.Extensions.Rpc.nuspec +++ b/Build/MQTTnet.Extensions.Rpc.nuspec @@ -35,6 +35,9 @@ + + + diff --git a/Build/MQTTnet.Extensions.WebSocket4Net.nuspec b/Build/MQTTnet.Extensions.WebSocket4Net.nuspec index 7d150148..640b6909 100644 --- a/Build/MQTTnet.Extensions.WebSocket4Net.nuspec +++ b/Build/MQTTnet.Extensions.WebSocket4Net.nuspec @@ -15,22 +15,8 @@ Copyright Christian Kratky 2016-2020 MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor - - - - - - - - - - - - - - - - + + @@ -50,6 +36,9 @@ + + + diff --git a/Build/MQTTnet.nuspec b/Build/MQTTnet.nuspec index 12e117ff..dce5048a 100644 --- a/Build/MQTTnet.nuspec +++ b/Build/MQTTnet.nuspec @@ -20,30 +20,18 @@ Git commit: $gitCommit Copyright Christian Kratky 2016-2020 MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor + + - - - - - - - - - - - - - - - - - - - + + + + + diff --git a/Build/build.ps1 b/Build/build.ps1 index 75907bdf..b1d3cc94 100644 --- a/Build/build.ps1 +++ b/Build/build.ps1 @@ -102,7 +102,7 @@ Remove-Item "nuget.exe" -Force -Recurse -ErrorAction SilentlyContinue # Build MQTTnet.Server Portable &dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --framework net5.0 -$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\publish" +$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\net5.0\publish" $destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Portable-v$nugetVersion.zip" If(Test-path $destination) {Remove-item $destination} Add-Type -assembly "system.io.compression.filesystem" @@ -113,7 +113,7 @@ If(Test-path $destination) {Remove-item $destination} # Build MQTTnet.Server Linux-x64 &dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime linux-x64 --framework net5.0 -$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\linux-x64\publish" +$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\net5.0\linux-x64\publish" $destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Linux-x64-v$nugetVersion.zip" If(Test-path $destination) {Remove-item $destination} Add-Type -assembly "system.io.compression.filesystem" @@ -124,7 +124,7 @@ If(Test-path $destination) {Remove-item $destination} # Build MQTTnet.Server Linux-ARM &dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime linux-arm --framework net5.0 -$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\linux-ARM\publish" +$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\net5.0\linux-ARM\publish" $destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Linux-ARM-v$nugetVersion.zip" If(Test-path $destination) {Remove-item $destination} Add-Type -assembly "system.io.compression.filesystem" @@ -135,7 +135,7 @@ If(Test-path $destination) {Remove-item $destination} # Build MQTTnet.Server Windows-x64 &dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime win-x64 --framework net5.0 -$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\win-x64\publish" +$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\net5.0\win-x64\publish" $destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Windows-x64-v$nugetVersion.zip" If(Test-path $destination) {Remove-item $destination} Add-Type -assembly "system.io.compression.filesystem" diff --git a/Source/MQTTnet/MQTTnet.csproj b/Source/MQTTnet/MQTTnet.csproj index 42989f38..8efd8727 100644 --- a/Source/MQTTnet/MQTTnet.csproj +++ b/Source/MQTTnet/MQTTnet.csproj @@ -35,14 +35,6 @@ Full - - - - - - - RELEASE;NETSTANDARD1_3 - @@ -54,20 +46,8 @@ - - - - - - - - - - - - \ No newline at end of file From 120b7fb7d1e2bac5cfdce5c7d5ec741780dee5eb Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 18 Feb 2021 18:56:15 +0100 Subject: [PATCH 26/29] Fix RPC issue #1093 by calling both message handlers. --- .../MQTTnet.Extensions.Rpc/MqttRpcClient.cs | 22 +++++++++++-------- ...cAwareApplicationMessageReceivedHandler.cs | 9 ++++---- ...MqttApplicationMessageReceivedEventArgs.cs | 8 +++++++ 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/Source/MQTTnet.Extensions.Rpc/MqttRpcClient.cs b/Source/MQTTnet.Extensions.Rpc/MqttRpcClient.cs index c225230e..3fbec759 100644 --- a/Source/MQTTnet.Extensions.Rpc/MqttRpcClient.cs +++ b/Source/MQTTnet.Extensions.Rpc/MqttRpcClient.cs @@ -8,6 +8,7 @@ using System.Collections.Concurrent; using System.Text; using System.Threading; using System.Threading.Tasks; +using MQTTnet.Implementations; namespace MQTTnet.Extensions.Rpc { @@ -88,8 +89,8 @@ namespace MQTTnet.Extensions.Rpc try { - var tcs = new TaskCompletionSource(); - if (!_waitingCalls.TryAdd(responseTopic, tcs)) + var promise = new TaskCompletionSource(); + if (!_waitingCalls.TryAdd(responseTopic, promise)) { throw new InvalidOperationException(); } @@ -102,15 +103,15 @@ namespace MQTTnet.Extensions.Rpc { linkedCts.Token.Register(() => { - if (!tcs.Task.IsCompleted && !tcs.Task.IsFaulted && !tcs.Task.IsCanceled) + if (!promise.Task.IsCompleted && !promise.Task.IsFaulted && !promise.Task.IsCanceled) { - tcs.TrySetCanceled(); + promise.TrySetCanceled(); } }); try { - var result = await tcs.Task.ConfigureAwait(false); + var result = await promise.Task.ConfigureAwait(false); timeoutCts.Cancel(false); return result; } @@ -136,14 +137,17 @@ namespace MQTTnet.Extensions.Rpc Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs) { - if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var tcs)) + if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var promise)) { - return Task.FromResult(0); + return PlatformAbstractionLayer.CompletedTask; } - tcs.TrySetResult(eventArgs.ApplicationMessage.Payload); + promise.TrySetResult(eventArgs.ApplicationMessage.Payload); - return Task.FromResult(0); + // Set this message to handled to that other code can avoid execution etc. + eventArgs.IsHandled = true; + + return PlatformAbstractionLayer.CompletedTask; } public void Dispose() diff --git a/Source/MQTTnet.Extensions.Rpc/RpcAwareApplicationMessageReceivedHandler.cs b/Source/MQTTnet.Extensions.Rpc/RpcAwareApplicationMessageReceivedHandler.cs index d7b75fc9..5d43eb68 100644 --- a/Source/MQTTnet.Extensions.Rpc/RpcAwareApplicationMessageReceivedHandler.cs +++ b/Source/MQTTnet.Extensions.Rpc/RpcAwareApplicationMessageReceivedHandler.cs @@ -18,14 +18,15 @@ namespace MQTTnet.Extensions.Rpc public IMqttApplicationMessageReceivedHandler OriginalHandler { get; } - public Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs) + public async Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs) { + // First try to check if there is a pending RPC call! + await _handleReceivedApplicationMessageAsync(eventArgs).ConfigureAwait(false); + if (OriginalHandler != null) { - return OriginalHandler.HandleApplicationMessageReceivedAsync(eventArgs); + await OriginalHandler.HandleApplicationMessageReceivedAsync(eventArgs).ConfigureAwait(false); } - - return _handleReceivedApplicationMessageAsync(eventArgs); } } } diff --git a/Source/MQTTnet/MqttApplicationMessageReceivedEventArgs.cs b/Source/MQTTnet/MqttApplicationMessageReceivedEventArgs.cs index 533f4df6..c44f1793 100644 --- a/Source/MQTTnet/MqttApplicationMessageReceivedEventArgs.cs +++ b/Source/MQTTnet/MqttApplicationMessageReceivedEventArgs.cs @@ -21,5 +21,13 @@ namespace MQTTnet public bool ProcessingFailed { get; set; } public MqttApplicationMessageReceivedReasonCode ReasonCode { get; set; } = MqttApplicationMessageReceivedReasonCode.Success; + + ///

+ /// Gets or sets whether this message was handled. + /// This value can be used in user code for custom control flow. + /// + public bool IsHandled { get; set; } + + public object Tag { get; set; } } } From cfc4ebe6a3cad043276a644eeeca3443f96f83a6 Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 19 Feb 2021 21:39:23 +0100 Subject: [PATCH 27/29] Expose Dup flag in application messages (#1096). --- Build/MQTTnet.nuspec | 2 + Source/MQTTnet/MqttApplicationMessage.cs | 8 ++++ .../MQTTnet/MqttApplicationMessageBuilder.cs | 45 +++++++++++-------- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/Build/MQTTnet.nuspec b/Build/MQTTnet.nuspec index 12e117ff..f29fe20a 100644 --- a/Build/MQTTnet.nuspec +++ b/Build/MQTTnet.nuspec @@ -14,6 +14,8 @@ * [Core] Fixed some issues in nuget packages. * [Client] Changed exception types so that proper exceptions are thrown when connecting (#1082). +* [Client] Exposed Dup flag in application messages. +* [RpcClient] Fixed an issue when using a custom application message reveived handler (#1093). Git commit: $gitCommit diff --git a/Source/MQTTnet/MqttApplicationMessage.cs b/Source/MQTTnet/MqttApplicationMessage.cs index 99068fe5..d1fe2297 100644 --- a/Source/MQTTnet/MqttApplicationMessage.cs +++ b/Source/MQTTnet/MqttApplicationMessage.cs @@ -35,6 +35,14 @@ namespace MQTTnet /// The broker stores the last retained message and the corresponding QoS for that topic. ///
public bool Retain { get; set; } + + /// + /// If the DUP flag is set to 0, it indicates that this is the first occasion that the Client or Server has attempted to send this MQTT PUBLISH Packet. + /// If the DUP flag is set to 1, it indicates that this might be re-delivery of an earlier attempt to send the Packet. + /// The DUP flag MUST be set to 1 by the Client or Server when it attempts to re-deliver a PUBLISH Packet [MQTT-3.3.1.-1]. + /// The DUP flag MUST be set to 0 for all QoS 0 messages [MQTT-3.3.1-2]. + /// + public bool Dup { get; set; } /// /// Gets or sets the user properties. diff --git a/Source/MQTTnet/MqttApplicationMessageBuilder.cs b/Source/MQTTnet/MqttApplicationMessageBuilder.cs index f2fc0cd4..5254dd19 100644 --- a/Source/MQTTnet/MqttApplicationMessageBuilder.cs +++ b/Source/MQTTnet/MqttApplicationMessageBuilder.cs @@ -19,54 +19,56 @@ namespace MQTTnet /// - At least once (1): Message gets delivered at least once (one time or more often). /// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once). /// - private MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce; + MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce; /// /// The MQTT topic. /// In MQTT, the word topic refers to an UTF-8 string that the broker uses to filter messages for each connected client. /// The topic consists of one or more topic levels. Each topic level is separated by a forward slash (topic level separator). /// - private string _topic; + string _topic; /// /// The payload. /// The payload is the data bytes sent via the MQTT protocol. /// - private byte[] _payload; + byte[] _payload; /// /// A value indicating whether the message should be retained or not. /// A retained message is a normal MQTT message with the retained flag set to true. /// The broker stores the last retained message and the corresponding QoS for that topic. /// - private bool _retain; + bool _retain; + + bool _dup; /// /// The content type. /// The content type must be a UTF-8 encoded string. The content type value identifies the kind of UTF-8 encoded payload. /// - private string _contentType; + string _contentType; /// /// The response topic. /// In MQTT 5 the ability to publish a response topic was added in the publish message which allows you to implement the request/response pattern between clients that is common in web applications. /// Hint: MQTT 5 feature only. /// - private string _responseTopic; + string _responseTopic; /// /// The correlation data. /// In order for the sender to know what sent message the response refers to it can also send correlation data with the published message. /// Hint: MQTT 5 feature only. /// - private byte[] _correlationData; + byte[] _correlationData; /// /// The topic alias. /// Topic aliases were introduced are a mechanism for reducing the size of published packets by reducing the size of the topic field. /// Hint: MQTT 5 feature only. /// - private ushort? _topicAlias; + ushort? _topicAlias; /// /// The subscription identifiers. @@ -75,7 +77,7 @@ namespace MQTTnet /// The broker will return the subscription identifier associated with this PUBLISH packet and the PUBLISH packet to the client when need to forward PUBLISH packets matching this subscription to this client. /// Hint: MQTT 5 feature only. /// - private List _subscriptionIdentifiers; + List _subscriptionIdentifiers; /// /// The message expiry interval. @@ -85,7 +87,7 @@ namespace MQTTnet /// When the retained=true option is set on the PUBLISH message, this interval also defines how long a message is retained on a topic. /// Hint: MQTT 5 feature only. /// - private uint? _messageExpiryInterval; + uint? _messageExpiryInterval; /// /// The payload format indicator. @@ -95,7 +97,7 @@ namespace MQTTnet /// If no payload format indicator is provided, the default value is 0. /// Hint: MQTT 5 feature only. /// - private MqttPayloadFormatIndicator? _payloadFormatIndicator; + MqttPayloadFormatIndicator? _payloadFormatIndicator; /// /// The user properties. @@ -104,7 +106,7 @@ namespace MQTTnet /// The feature is very similar to the HTTP header concept. /// /// Hint: MQTT 5 feature only. - private List _userProperties; + List _userProperties; public MqttApplicationMessageBuilder WithTopic(string topic) { @@ -126,12 +128,7 @@ namespace MQTTnet return this; } - _payload = payload as byte[]; - - if (_payload == null) - { - _payload = payload.ToArray(); - } + _payload = payload as byte[] ?? payload.ToArray(); return this; } @@ -201,6 +198,12 @@ namespace MQTTnet _retain = value; return this; } + + public MqttApplicationMessageBuilder WithDupFlag(bool value = true) + { + _dup = value; + return this; + } public MqttApplicationMessageBuilder WithAtLeastOnceQoS() { @@ -338,12 +341,18 @@ namespace MQTTnet throw new MqttProtocolViolationException("A Topic Alias of 0 is not permitted. A sender MUST NOT send a PUBLISH packet containing a Topic Alias which has the value 0 [MQTT-3.3.2-8]."); } + if (_qualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce && _dup) + { + throw new MqttProtocolViolationException("The DUP flag MUST be set to 0 for all QoS 0 messages [MQTT-3.3.1-2]."); + } + var applicationMessage = new MqttApplicationMessage { Topic = _topic, Payload = _payload, QualityOfServiceLevel = _qualityOfServiceLevel, Retain = _retain, + Dup = _dup, ContentType = _contentType, ResponseTopic = _responseTopic, CorrelationData = _correlationData, From ed520455d0b5686d32a6e8d4ebc1f30c93c847a2 Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 19 Feb 2021 21:45:23 +0100 Subject: [PATCH 28/29] Use the data converter only when creating packets. --- .../MqttClientDisconnectReason.cs | 2 +- Source/MQTTnet/Client/MqttClient.cs | 44 +++++-------- .../MQTTnet/Formatter/IMqttDataConverter.cs | 30 +++++---- .../Formatter/V3/MqttV310DataConverter.cs | 47 +++++++++++--- .../Formatter/V5/MqttV500DataConverter.cs | 61 +++++++++++++++---- ...qttApplicationMessageReceivedReasonCode.cs | 1 + Source/MQTTnet/Packets/MqttPingReqPacket.cs | 2 +- Source/MQTTnet/Packets/MqttPingRespPacket.cs | 2 +- Source/MQTTnet/Server/MqttClientConnection.cs | 43 ++++++------- .../Server/MqttClientSessionsManager.cs | 9 +-- .../Server/MqttServerKeepAliveMonitor.cs | 4 +- .../MQTTnet/Server/Status/MqttClientStatus.cs | 4 +- 12 files changed, 153 insertions(+), 96 deletions(-) diff --git a/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectReason.cs b/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectReason.cs index e1ea30d7..cce2377f 100644 --- a/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectReason.cs +++ b/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectReason.cs @@ -12,7 +12,7 @@ ServerBusy = 137, ServerShuttingDown = 139, BadAuthenticationMethod = 140, - KeepaliveTimeout = 141, + KeepAliveTimeout = 141, SessionTakenOver = 142, TopicFilterInvalid = 143, TopicNameInvalid = 144, diff --git a/Source/MQTTnet/Client/MqttClient.cs b/Source/MQTTnet/Client/MqttClient.cs index 3414e990..b16cac7e 100644 --- a/Source/MQTTnet/Client/MqttClient.cs +++ b/Source/MQTTnet/Client/MqttClient.cs @@ -167,7 +167,7 @@ namespace MQTTnet.Client public Task PingAsync(CancellationToken cancellationToken) { - return SendAndReceiveAsync(new MqttPingReqPacket(), cancellationToken); + return SendAndReceiveAsync(MqttPingReqPacket.Instance, cancellationToken); } public Task SendExtendedAuthenticationExchangeDataAsync(MqttExtendedAuthenticationExchangeData data, CancellationToken cancellationToken) @@ -650,11 +650,8 @@ namespace MQTTnet.Client if (!eventArgs.ProcessingFailed) { - await SendAsync(new MqttPubAckPacket - { - PacketIdentifier = publishPacket.PacketIdentifier, - ReasonCode = (MqttPubAckReasonCode)eventArgs.ReasonCode - }, cancellationToken).ConfigureAwait(false); + var pubAckPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubAckPacket(publishPacket, eventArgs.ReasonCode); + await SendAsync(pubAckPacket, cancellationToken).ConfigureAwait(false); } } else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce) @@ -663,11 +660,8 @@ namespace MQTTnet.Client if (!eventArgs.ProcessingFailed) { - await SendAsync(new MqttPubRecPacket - { - PacketIdentifier = publishPacket.PacketIdentifier, - ReasonCode = (MqttPubRecReasonCode)eventArgs.ReasonCode - }, cancellationToken).ConfigureAwait(false); + var pubRecPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRecPacket(publishPacket, eventArgs.ReasonCode); + await SendAsync(pubRecPacket, cancellationToken).ConfigureAwait(false); } } else @@ -691,11 +685,8 @@ namespace MQTTnet.Client { // The packet is unknown. Probably due to a restart of the client. // So wen send this to the server to trigger a full resend of the message. - return SendAsync(new MqttPubRelPacket - { - PacketIdentifier = pubRecPacket.PacketIdentifier, - ReasonCode = MqttPubRelReasonCode.PacketIdentifierNotFound - }, cancellationToken); + var pubRelPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRelPacket(pubRecPacket, MqttApplicationMessageReceivedReasonCode.PacketIdentifierNotFound); + return SendAsync(pubRelPacket, cancellationToken); } return PlatformAbstractionLayer.CompletedTask; @@ -703,11 +694,8 @@ namespace MQTTnet.Client Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken) { - return SendAsync(new MqttPubCompPacket - { - PacketIdentifier = pubRelPacket.PacketIdentifier, - ReasonCode = MqttPubCompReasonCode.Success - }, cancellationToken); + var pubCompPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubCompPacket(pubRelPacket, MqttApplicationMessageReceivedReasonCode.Success); + return SendAsync(pubCompPacket, cancellationToken); } Task ProcessReceivedDisconnectPacket(MqttDisconnectPacket disconnectPacket) @@ -740,14 +728,14 @@ namespace MQTTnet.Client { // No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier] await SendAsync(publishPacket, cancellationToken).ConfigureAwait(false); - return _adapter.PacketFormatterAdapter.DataConverter.CreatePublishResult(null); + return _adapter.PacketFormatterAdapter.DataConverter.CreateClientPublishResult(null); } async Task PublishAtLeastOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken) { publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier(); var response = await SendAndReceiveAsync(publishPacket, cancellationToken).ConfigureAwait(false); - return _adapter.PacketFormatterAdapter.DataConverter.CreatePublishResult(response); + return _adapter.PacketFormatterAdapter.DataConverter.CreateClientPublishResult(response); } async Task PublishExactlyOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken) @@ -756,15 +744,11 @@ namespace MQTTnet.Client var pubRecPacket = await SendAndReceiveAsync(publishPacket, cancellationToken).ConfigureAwait(false); - var pubRelPacket = new MqttPubRelPacket - { - PacketIdentifier = publishPacket.PacketIdentifier, - ReasonCode = MqttPubRelReasonCode.Success - }; - + var pubRelPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRelPacket(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success); + var pubCompPacket = await SendAndReceiveAsync(pubRelPacket, cancellationToken).ConfigureAwait(false); - return _adapter.PacketFormatterAdapter.DataConverter.CreatePublishResult(pubRecPacket, pubCompPacket); + return _adapter.PacketFormatterAdapter.DataConverter.CreateClientPublishResult(pubRecPacket, pubCompPacket); } async Task HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket) diff --git a/Source/MQTTnet/Formatter/IMqttDataConverter.cs b/Source/MQTTnet/Formatter/IMqttDataConverter.cs index 76079a1b..aec47027 100644 --- a/Source/MQTTnet/Formatter/IMqttDataConverter.cs +++ b/Source/MQTTnet/Formatter/IMqttDataConverter.cs @@ -14,24 +14,32 @@ namespace MQTTnet.Formatter { public interface IMqttDataConverter { - MqttPublishPacket CreatePublishPacket(MqttApplicationMessage applicationMessage); - - MqttPubAckPacket CreatePubAckPacket(MqttPublishPacket publishPacket); - - MqttBasePacket CreatePubRecPacket(MqttPublishPacket publishPacket); - MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket); MqttClientAuthenticateResult CreateClientConnectResult(MqttConnAckPacket connAckPacket); + MqttClientPublishResult CreateClientPublishResult(MqttPubAckPacket pubAckPacket); + + MqttClientPublishResult CreateClientPublishResult(MqttPubRecPacket pubRecPacket, MqttPubCompPacket pubCompPacket); + + MqttClientSubscribeResult CreateClientSubscribeResult(MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket); + + MqttClientUnsubscribeResult CreateClientUnsubscribeResult(MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket); + MqttConnectPacket CreateConnectPacket(MqttApplicationMessage willApplicationMessage, IMqttClientOptions options); MqttConnAckPacket CreateConnAckPacket(MqttConnectionValidatorContext connectionValidatorContext); + + MqttPublishPacket CreatePublishPacket(MqttApplicationMessage applicationMessage); - MqttClientSubscribeResult CreateClientSubscribeResult(MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket); - - MqttClientUnsubscribeResult CreateClientUnsubscribeResult(MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket); + MqttPubAckPacket CreatePubAckPacket(MqttPublishPacket publishPacket, MqttApplicationMessageReceivedReasonCode reasonCode); + + MqttPubRecPacket CreatePubRecPacket(MqttPublishPacket publishPacket, MqttApplicationMessageReceivedReasonCode reasonCode); + MqttPubCompPacket CreatePubCompPacket(MqttPubRelPacket pubRelPacket, MqttApplicationMessageReceivedReasonCode reasonCode); + + MqttPubRelPacket CreatePubRelPacket(MqttPubRecPacket pubRecPacket, MqttApplicationMessageReceivedReasonCode reasonCode); + MqttSubscribePacket CreateSubscribePacket(MqttClientSubscribeOptions options); MqttSubAckPacket CreateSubAckPacket(MqttSubscribePacket subscribePacket, Server.MqttClientSubscribeResult subscribeResult); @@ -41,9 +49,5 @@ namespace MQTTnet.Formatter MqttUnsubAckPacket CreateUnsubAckPacket(MqttUnsubscribePacket unsubscribePacket, List reasonCodes); MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options); - - MqttClientPublishResult CreatePublishResult(MqttPubAckPacket pubAckPacket); - - MqttClientPublishResult CreatePublishResult(MqttPubRecPacket pubRecPacket, MqttPubCompPacket pubCompPacket); } } diff --git a/Source/MQTTnet/Formatter/V3/MqttV310DataConverter.cs b/Source/MQTTnet/Formatter/V3/MqttV310DataConverter.cs index 31ea9ed4..c2b16747 100644 --- a/Source/MQTTnet/Formatter/V3/MqttV310DataConverter.cs +++ b/Source/MQTTnet/Formatter/V3/MqttV310DataConverter.cs @@ -31,21 +31,43 @@ namespace MQTTnet.Formatter.V3 }; } - public MqttPubAckPacket CreatePubAckPacket(MqttPublishPacket publishPacket) + public MqttPubAckPacket CreatePubAckPacket(MqttPublishPacket publishPacket, MqttApplicationMessageReceivedReasonCode reasonCode) { + if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket)); + return new MqttPubAckPacket { - PacketIdentifier = publishPacket.PacketIdentifier, - ReasonCode = MqttPubAckReasonCode.Success + PacketIdentifier = publishPacket.PacketIdentifier }; } - public MqttBasePacket CreatePubRecPacket(MqttPublishPacket publishPacket) + public MqttPubRecPacket CreatePubRecPacket(MqttPublishPacket publishPacket, MqttApplicationMessageReceivedReasonCode reasonCode) { + if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket)); + return new MqttPubRecPacket { - PacketIdentifier = publishPacket.PacketIdentifier, - ReasonCode = MqttPubRecReasonCode.Success + PacketIdentifier = publishPacket.PacketIdentifier + }; + } + + public MqttPubCompPacket CreatePubCompPacket(MqttPubRelPacket pubRelPacket, MqttApplicationMessageReceivedReasonCode reasonCode) + { + if (pubRelPacket == null) throw new ArgumentNullException(nameof(pubRelPacket)); + + return new MqttPubCompPacket + { + PacketIdentifier = pubRelPacket.PacketIdentifier + }; + } + + public MqttPubRelPacket CreatePubRelPacket(MqttPubRecPacket pubRecPacket, MqttApplicationMessageReceivedReasonCode reasonCode) + { + if (pubRecPacket == null) throw new ArgumentNullException(nameof(pubRecPacket)); + + return new MqttPubRelPacket + { + PacketIdentifier = pubRecPacket.PacketIdentifier }; } @@ -58,7 +80,8 @@ namespace MQTTnet.Formatter.V3 Topic = publishPacket.Topic, Payload = publishPacket.Payload, QualityOfServiceLevel = publishPacket.QualityOfServiceLevel, - Retain = publishPacket.Retain + Retain = publishPacket.Retain, + Dup = publishPacket.Dup }; } @@ -184,6 +207,9 @@ namespace MQTTnet.Formatter.V3 public MqttSubAckPacket CreateSubAckPacket(MqttSubscribePacket subscribePacket, Server.MqttClientSubscribeResult subscribeResult) { + if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket)); + if (subscribeResult == null) throw new ArgumentNullException(nameof(subscribeResult)); + var subackPacket = new MqttSubAckPacket { PacketIdentifier = subscribePacket.PacketIdentifier @@ -206,6 +232,9 @@ namespace MQTTnet.Formatter.V3 public MqttUnsubAckPacket CreateUnsubAckPacket(MqttUnsubscribePacket unsubscribePacket, List reasonCodes) { + if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket)); + if (reasonCodes == null) throw new ArgumentNullException(nameof(reasonCodes)); + return new MqttUnsubAckPacket { PacketIdentifier = unsubscribePacket.PacketIdentifier, @@ -223,7 +252,7 @@ namespace MQTTnet.Formatter.V3 return new MqttDisconnectPacket(); } - public MqttClientPublishResult CreatePublishResult(MqttPubAckPacket pubAckPacket) + public MqttClientPublishResult CreateClientPublishResult(MqttPubAckPacket pubAckPacket) { return new MqttClientPublishResult { @@ -232,7 +261,7 @@ namespace MQTTnet.Formatter.V3 }; } - public MqttClientPublishResult CreatePublishResult(MqttPubRecPacket pubRecPacket, MqttPubCompPacket pubCompPacket) + public MqttClientPublishResult CreateClientPublishResult(MqttPubRecPacket pubRecPacket, MqttPubCompPacket pubCompPacket) { if (pubRecPacket == null || pubCompPacket == null) { diff --git a/Source/MQTTnet/Formatter/V5/MqttV500DataConverter.cs b/Source/MQTTnet/Formatter/V5/MqttV500DataConverter.cs index 038a4ed9..49da6fcd 100644 --- a/Source/MQTTnet/Formatter/V5/MqttV500DataConverter.cs +++ b/Source/MQTTnet/Formatter/V5/MqttV500DataConverter.cs @@ -15,7 +15,7 @@ using MqttClientSubscribeResult = MQTTnet.Client.Subscribing.MqttClientSubscribe namespace MQTTnet.Formatter.V5 { - public class MqttV500DataConverter : IMqttDataConverter + public sealed class MqttV500DataConverter : IMqttDataConverter { public MqttPublishPacket CreatePublishPacket(MqttApplicationMessage applicationMessage) { @@ -27,7 +27,7 @@ namespace MQTTnet.Formatter.V5 Payload = applicationMessage.Payload, QualityOfServiceLevel = applicationMessage.QualityOfServiceLevel, Retain = applicationMessage.Retain, - Dup = false, + Dup = applicationMessage.Dup, Properties = new MqttPublishPacketProperties { ContentType = applicationMessage.ContentType, @@ -49,32 +49,61 @@ namespace MQTTnet.Formatter.V5 return packet; } - public MqttPubAckPacket CreatePubAckPacket(MqttPublishPacket publishPacket) + public MqttPubAckPacket CreatePubAckPacket(MqttPublishPacket publishPacket, MqttApplicationMessageReceivedReasonCode reasonCode) { + if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket)); + return new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier, - ReasonCode = MqttPubAckReasonCode.Success + ReasonCode = (MqttPubAckReasonCode)(int)reasonCode }; } - public MqttBasePacket CreatePubRecPacket(MqttPublishPacket publishPacket) + public MqttPubRecPacket CreatePubRecPacket(MqttPublishPacket publishPacket, MqttApplicationMessageReceivedReasonCode reasonCode) { + if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket)); + return new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier, - ReasonCode = MqttPubRecReasonCode.Success + ReasonCode = (MqttPubRecReasonCode)(int)reasonCode + }; + } + + public MqttPubCompPacket CreatePubCompPacket(MqttPubRelPacket pubRelPacket, MqttApplicationMessageReceivedReasonCode reasonCode) + { + if (pubRelPacket == null) throw new ArgumentNullException(nameof(pubRelPacket)); + + return new MqttPubCompPacket + { + PacketIdentifier = pubRelPacket.PacketIdentifier, + ReasonCode = (MqttPubCompReasonCode)(int)reasonCode + }; + } + + public MqttPubRelPacket CreatePubRelPacket(MqttPubRecPacket pubRecPacket, MqttApplicationMessageReceivedReasonCode reasonCode) + { + if (pubRecPacket == null) throw new ArgumentNullException(nameof(pubRecPacket)); + + return new MqttPubRelPacket + { + PacketIdentifier = pubRecPacket.PacketIdentifier, + ReasonCode = (MqttPubRelReasonCode)(int)reasonCode }; } public MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket) { + if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket)); + return new MqttApplicationMessage { Topic = publishPacket.Topic, Payload = publishPacket.Payload, QualityOfServiceLevel = publishPacket.QualityOfServiceLevel, Retain = publishPacket.Retain, + Dup = publishPacket.Dup, ResponseTopic = publishPacket.Properties?.ResponseTopic, ContentType = publishPacket.Properties?.ContentType, CorrelationData = publishPacket.Properties?.CorrelationData, @@ -93,7 +122,7 @@ namespace MQTTnet.Formatter.V5 return new MqttClientAuthenticateResult { IsSessionPresent = connAckPacket.IsSessionPresent, - ResultCode = (MqttClientConnectResultCode)connAckPacket.ReasonCode.Value, + ResultCode = (MqttClientConnectResultCode)(int)(connAckPacket.ReasonCode ?? 0), WildcardSubscriptionAvailable = connAckPacket.Properties?.WildcardSubscriptionAvailable, RetainAvailable = connAckPacket.Properties?.RetainAvailable, AssignedClientIdentifier = connAckPacket.Properties?.AssignedClientIdentifier, @@ -144,6 +173,8 @@ namespace MQTTnet.Formatter.V5 public MqttConnAckPacket CreateConnAckPacket(MqttConnectionValidatorContext connectionValidatorContext) { + if (connectionValidatorContext == null) throw new ArgumentNullException(nameof(connectionValidatorContext)); + return new MqttConnAckPacket { ReasonCode = connectionValidatorContext.ReasonCode, @@ -213,6 +244,9 @@ namespace MQTTnet.Formatter.V5 public MqttSubAckPacket CreateSubAckPacket(MqttSubscribePacket subscribePacket, Server.MqttClientSubscribeResult subscribeResult) { + if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket)); + if (subscribeResult == null) throw new ArgumentNullException(nameof(subscribeResult)); + var subackPacket = new MqttSubAckPacket { PacketIdentifier = subscribePacket.PacketIdentifier @@ -240,6 +274,9 @@ namespace MQTTnet.Formatter.V5 public MqttUnsubAckPacket CreateUnsubAckPacket(MqttUnsubscribePacket unsubscribePacket, List reasonCodes) { + if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket)); + if (reasonCodes == null) throw new ArgumentNullException(nameof(reasonCodes)); + return new MqttUnsubAckPacket { PacketIdentifier = unsubscribePacket.PacketIdentifier, @@ -263,7 +300,7 @@ namespace MQTTnet.Formatter.V5 return packet; } - public MqttClientPublishResult CreatePublishResult(MqttPubAckPacket pubAckPacket) + public MqttClientPublishResult CreateClientPublishResult(MqttPubAckPacket pubAckPacket) { var result = new MqttClientPublishResult { @@ -276,7 +313,7 @@ namespace MQTTnet.Formatter.V5 { // QoS 0 has no response. So we treat it as a success always. // Both enums have the same values. So it can be easily converted. - result.ReasonCode = (MqttClientPublishReasonCode)pubAckPacket.ReasonCode; + result.ReasonCode = (MqttClientPublishReasonCode)(int)(pubAckPacket.ReasonCode ?? 0); result.PacketIdentifier = pubAckPacket.PacketIdentifier; } @@ -284,7 +321,7 @@ namespace MQTTnet.Formatter.V5 return result; } - public MqttClientPublishResult CreatePublishResult(MqttPubRecPacket pubRecPacket, MqttPubCompPacket pubCompPacket) + public MqttClientPublishResult CreateClientPublishResult(MqttPubRecPacket pubRecPacket, MqttPubCompPacket pubCompPacket) { if (pubRecPacket == null || pubCompPacket == null) { @@ -317,10 +354,10 @@ namespace MQTTnet.Formatter.V5 if (pubRecPacket.ReasonCode.HasValue) { // Both enums share the same values. - result.ReasonCode = (MqttClientPublishReasonCode)pubRecPacket.ReasonCode.Value; + result.ReasonCode = (MqttClientPublishReasonCode)(pubRecPacket.ReasonCode ?? 0); } return result; } } -} +} \ No newline at end of file diff --git a/Source/MQTTnet/MqttApplicationMessageReceivedReasonCode.cs b/Source/MQTTnet/MqttApplicationMessageReceivedReasonCode.cs index b192d311..3cd6e69d 100644 --- a/Source/MQTTnet/MqttApplicationMessageReceivedReasonCode.cs +++ b/Source/MQTTnet/MqttApplicationMessageReceivedReasonCode.cs @@ -9,6 +9,7 @@ NotAuthorized = 135, TopicNameInvalid = 144, PacketIdentifierInUse = 145, + PacketIdentifierNotFound = 146, QuotaExceeded = 151, PayloadFormatInvalid = 153 } diff --git a/Source/MQTTnet/Packets/MqttPingReqPacket.cs b/Source/MQTTnet/Packets/MqttPingReqPacket.cs index 459e8b91..18a3a162 100644 --- a/Source/MQTTnet/Packets/MqttPingReqPacket.cs +++ b/Source/MQTTnet/Packets/MqttPingReqPacket.cs @@ -3,7 +3,7 @@ public sealed class MqttPingReqPacket : MqttBasePacket { // This is a minor performance improvement. - public static MqttPingReqPacket Instance = new MqttPingReqPacket(); + public static readonly MqttPingReqPacket Instance = new MqttPingReqPacket(); public override string ToString() { diff --git a/Source/MQTTnet/Packets/MqttPingRespPacket.cs b/Source/MQTTnet/Packets/MqttPingRespPacket.cs index de1db5e2..8ddc349f 100644 --- a/Source/MQTTnet/Packets/MqttPingRespPacket.cs +++ b/Source/MQTTnet/Packets/MqttPingRespPacket.cs @@ -3,7 +3,7 @@ public sealed class MqttPingRespPacket : MqttBasePacket { // This is a minor performance improvement. - public static MqttPingRespPacket Instance = new MqttPingRespPacket(); + public static readonly MqttPingRespPacket Instance = new MqttPingRespPacket(); public override string ToString() { diff --git a/Source/MQTTnet/Server/MqttClientConnection.cs b/Source/MQTTnet/Server/MqttClientConnection.cs index bd31f4b7..75083a54 100644 --- a/Source/MQTTnet/Server/MqttClientConnection.cs +++ b/Source/MQTTnet/Server/MqttClientConnection.cs @@ -13,6 +13,7 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using MQTTnet.Client.Disconnecting; namespace MQTTnet.Server { @@ -42,7 +43,7 @@ namespace MQTTnet.Server long _sentPacketsCount = 1; // Start with 1 because the CONNECT packet is not counted anywhere. long _receivedApplicationMessagesCount; long _sentApplicationMessagesCount; - MqttDisconnectReasonCode _disconnectReason; + MqttClientDisconnectReason _disconnectReason; public MqttClientConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter, @@ -84,12 +85,12 @@ namespace MQTTnet.Server public MqttClientSession Session { get; } - public async Task StopAsync(MqttDisconnectReasonCode reason) + public async Task StopAsync(MqttClientDisconnectReason reason) { Status = MqttClientConnectionStatus.Finalizing; _disconnectReason = reason; - if (reason == MqttDisconnectReasonCode.SessionTakenOver || reason == MqttDisconnectReasonCode.KeepAliveTimeout) + if (reason == MqttClientDisconnectReason.SessionTakenOver || reason == MqttClientDisconnectReason.KeepAliveTimeout) { // Is is very important to send the DISCONNECT packet here BEFORE cancelling the // token because the entire connection is closed (disposed) as soon as the cancellation @@ -97,10 +98,18 @@ namespace MQTTnet.Server // at the client! try { - await _channelAdapter.SendPacketAsync(new MqttDisconnectPacket + var disconnectOptions = new MqttClientDisconnectOptions { - ReasonCode = reason - }, CancellationToken.None).ConfigureAwait(false); + ReasonCode = reason, + ReasonString = reason.ToString() + }; + + var disconnectPacket = _channelAdapter.PacketFormatterAdapter.DataConverter.CreateDisconnectPacket(disconnectOptions); + + using (var timeout = new CancellationTokenSource(_serverOptions.DefaultCommunicationTimeout)) + { + await _channelAdapter.SendPacketAsync(disconnectPacket, timeout.Token).ConfigureAwait(false); + } } catch (Exception exception) { @@ -235,7 +244,7 @@ namespace MQTTnet.Server } finally { - if (_disconnectReason == MqttDisconnectReasonCode.SessionTakenOver) + if (_disconnectReason == MqttClientDisconnectReason.SessionTakenOver) { disconnectType = MqttClientDisconnectType.Takeover; } @@ -277,12 +286,7 @@ namespace MQTTnet.Server Task HandleIncomingPubRelPacketAsync(MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken) { - var pubCompPacket = new MqttPubCompPacket - { - PacketIdentifier = pubRelPacket.PacketIdentifier, - ReasonCode = MqttPubCompReasonCode.Success - }; - + var pubCompPacket = _channelAdapter.PacketFormatterAdapter.DataConverter.CreatePubCompPacket(pubRelPacket, MqttApplicationMessageReceivedReasonCode.Success); return SendAsync(pubCompPacket, cancellationToken); } @@ -327,12 +331,12 @@ namespace MQTTnet.Server } case MqttQualityOfServiceLevel.AtLeastOnce: { - var pubAckPacket = _dataConverter.CreatePubAckPacket(publishPacket); + var pubAckPacket = _dataConverter.CreatePubAckPacket(publishPacket, MqttApplicationMessageReceivedReasonCode.Success); return SendAsync(pubAckPacket, cancellationToken); } case MqttQualityOfServiceLevel.ExactlyOnce: { - var pubRecPacket = _dataConverter.CreatePubRecPacket(publishPacket); + var pubRecPacket = _dataConverter.CreatePubRecPacket(publishPacket, MqttApplicationMessageReceivedReasonCode.Success); return SendAsync(pubRecPacket, cancellationToken); } default: @@ -441,13 +445,10 @@ namespace MQTTnet.Server using (var awaiter2 = _packetDispatcher.AddAwaiter(publishPacket.PacketIdentifier)) { await SendAsync(publishPacket, cancellationToken).ConfigureAwait(false); - await awaiter1.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false); + var pubRecPacket = await awaiter1.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false); - await SendAsync(new MqttPubRelPacket - { - PacketIdentifier = publishPacket.PacketIdentifier, - ReasonCode = MqttPubRelReasonCode.Success - }, cancellationToken).ConfigureAwait(false); + var pubRelPacket = _channelAdapter.PacketFormatterAdapter.DataConverter.CreatePubRelPacket(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success); + await SendAsync(pubRelPacket, cancellationToken).ConfigureAwait(false); await awaiter2.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false); } diff --git a/Source/MQTTnet/Server/MqttClientSessionsManager.cs b/Source/MQTTnet/Server/MqttClientSessionsManager.cs index 619f65fc..2c3202bb 100644 --- a/Source/MQTTnet/Server/MqttClientSessionsManager.cs +++ b/Source/MQTTnet/Server/MqttClientSessionsManager.cs @@ -12,6 +12,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MQTTnet.Client.Disconnecting; using OperationCanceledException = System.OperationCanceledException; namespace MQTTnet.Server @@ -121,7 +122,7 @@ namespace MQTTnet.Server foreach (var connection in connections) { - await connection.StopAsync(MqttDisconnectReasonCode.NormalDisconnection).ConfigureAwait(false); + await connection.StopAsync(MqttClientDisconnectReason.NormalDisconnection).ConfigureAwait(false); } } @@ -211,7 +212,7 @@ namespace MQTTnet.Server if (connection != null) { - await connection.StopAsync(MqttDisconnectReasonCode.NormalDisconnection).ConfigureAwait(false); + await connection.StopAsync(MqttClientDisconnectReason.NormalDisconnection).ConfigureAwait(false); } _logger.Verbose("Session for client '{0}' deleted.", clientId); @@ -301,7 +302,7 @@ namespace MQTTnet.Server { if (clientConnection != null) { - await clientConnection.StopAsync(MqttDisconnectReasonCode.NormalDisconnection).ConfigureAwait(false); + await clientConnection.StopAsync(MqttClientDisconnectReason.NormalDisconnection).ConfigureAwait(false); } } @@ -424,7 +425,7 @@ namespace MQTTnet.Server _connections[connectPacket.ClientId] = connection; } - existingConnection?.StopAsync(MqttDisconnectReasonCode.SessionTakenOver).GetAwaiter().GetResult(); + existingConnection?.StopAsync(MqttClientDisconnectReason.SessionTakenOver).GetAwaiter().GetResult(); return connection; } diff --git a/Source/MQTTnet/Server/MqttServerKeepAliveMonitor.cs b/Source/MQTTnet/Server/MqttServerKeepAliveMonitor.cs index bd6eaa0a..7f967e9e 100644 --- a/Source/MQTTnet/Server/MqttServerKeepAliveMonitor.cs +++ b/Source/MQTTnet/Server/MqttServerKeepAliveMonitor.cs @@ -3,8 +3,8 @@ using MQTTnet.Internal; using System; using System.Threading; using System.Threading.Tasks; +using MQTTnet.Client.Disconnecting; using MQTTnet.Implementations; -using MQTTnet.Protocol; namespace MQTTnet.Server { @@ -108,7 +108,7 @@ namespace MQTTnet.Server // We do not need to wait for the task so no await is needed. // Also the internal state of the connection must be swapped to "Finalizing" because the // next iteration of the keep alive timer happens. - var _ = connection.StopAsync(MqttDisconnectReasonCode.KeepAliveTimeout); + var _ = connection.StopAsync(MqttClientDisconnectReason.KeepAliveTimeout); } catch (Exception exception) { diff --git a/Source/MQTTnet/Server/Status/MqttClientStatus.cs b/Source/MQTTnet/Server/Status/MqttClientStatus.cs index eca5d1a5..9f2f670e 100644 --- a/Source/MQTTnet/Server/Status/MqttClientStatus.cs +++ b/Source/MQTTnet/Server/Status/MqttClientStatus.cs @@ -1,7 +1,7 @@ using MQTTnet.Formatter; using System; using System.Threading.Tasks; -using MQTTnet.Protocol; +using MQTTnet.Client.Disconnecting; namespace MQTTnet.Server.Status { @@ -46,7 +46,7 @@ namespace MQTTnet.Server.Status public Task DisconnectAsync() { - return _connection.StopAsync(MqttDisconnectReasonCode.NormalDisconnection); + return _connection.StopAsync(MqttClientDisconnectReason.NormalDisconnection); } public void ResetStatistics() From 2d4ea8ee9b7a1ef65d04d99f62507ee77547b1a4 Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 19 Feb 2021 21:52:42 +0100 Subject: [PATCH 29/29] Improve nuget packages. --- Build/MQTTnet.AspNetCore.nuspec | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Build/MQTTnet.AspNetCore.nuspec b/Build/MQTTnet.AspNetCore.nuspec index e8e01d3b..f9bc8a04 100644 --- a/Build/MQTTnet.AspNetCore.nuspec +++ b/Build/MQTTnet.AspNetCore.nuspec @@ -28,14 +28,6 @@ - - - - - - - -