diff --git a/MQTTnet.sln b/MQTTnet.sln index be6a0b6..309b11f 100644 --- a/MQTTnet.sln +++ b/MQTTnet.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27004.2010 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Core.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Core.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1-B9D6-40BF-81EC-86004D7765B4}" EndProject diff --git a/README.md b/README.md index 436628a..5b5288e 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,10 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov * Extensible communication channels (e.g. In-Memory, TCP, TCP+TLS, WS) * Lightweight (only the low level implementation of MQTT, no overhead) * Performance optimized (processing ~70.000 messages / second)* +* Uniform API over all versions of MQTT protocol * Interfaces included for mocking and testing * Access to internal trace messages -* Unit tested (~100 tests) +* Unit tested (~120 tests) \* Tested on local machine (Intel i7 8700K) with MQTTnet client and server running in the same process using the TCP channel. The app for verification is part of this repository and stored in _/Tests/MQTTnet.TestApp.NetCore_. diff --git a/Source/MQTTnet.AspnetCore/Client/MqttClientConnectionContextFactory.cs b/Source/MQTTnet.AspnetCore/Client/MqttClientConnectionContextFactory.cs index 93314f4..a83fcef 100644 --- a/Source/MQTTnet.AspnetCore/Client/MqttClientConnectionContextFactory.cs +++ b/Source/MQTTnet.AspnetCore/Client/MqttClientConnectionContextFactory.cs @@ -3,6 +3,7 @@ using System.Net; using MQTTnet.Adapter; using MQTTnet.AspNetCore.Client.Tcp; using MQTTnet.Client; +using MQTTnet.Client.Options; using MQTTnet.Diagnostics; using MQTTnet.Formatter; diff --git a/Source/MQTTnet.AspnetCore/ReaderExtensions.cs b/Source/MQTTnet.AspnetCore/ReaderExtensions.cs index abe8ef7..81d24c0 100644 --- a/Source/MQTTnet.AspnetCore/ReaderExtensions.cs +++ b/Source/MQTTnet.AspnetCore/ReaderExtensions.cs @@ -36,7 +36,7 @@ namespace MQTTnet.AspNetCore var bodySlice = copy.Slice(0, bodyLength); var buffer = bodySlice.GetArray(); - packet = formatter.Decode(new ReceivedMqttPacket(fixedheader, new MqttPacketBodyReader(buffer, 0, buffer.Length))); + packet = formatter.Decode(new ReceivedMqttPacket(fixedheader, new MqttPacketBodyReader(buffer, 0, buffer.Length), buffer.Length + 2)); consumed = bodySlice.End; observed = bodySlice.End; return true; diff --git a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs b/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs index b3835e3..12a6560 100644 --- a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs +++ b/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using MQTTnet.Client; +using MQTTnet.Client.Connecting; +using MQTTnet.Client.Disconnecting; namespace MQTTnet.Extensions.ManagedClient { diff --git a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClientOptions.cs b/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClientOptions.cs index f59aca7..589f7ac 100644 --- a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClientOptions.cs +++ b/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClientOptions.cs @@ -1,5 +1,6 @@ using System; using MQTTnet.Client; +using MQTTnet.Client.Options; using MQTTnet.Server; namespace MQTTnet.Extensions.ManagedClient diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClient.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClient.cs index 866a75d..e1feb8d 100644 --- a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClient.cs +++ b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClient.cs @@ -4,13 +4,14 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using MQTTnet.Client; +using MQTTnet.Client.Publishing; using MQTTnet.Diagnostics; using MQTTnet.Exceptions; using MQTTnet.Internal; using MQTTnet.Protocol; using MQTTnet.Server; -using MqttClientConnectedEventArgs = MQTTnet.Client.MqttClientConnectedEventArgs; -using MqttClientDisconnectedEventArgs = MQTTnet.Client.MqttClientDisconnectedEventArgs; +using MqttClientConnectedEventArgs = MQTTnet.Client.Connecting.MqttClientConnectedEventArgs; +using MqttClientDisconnectedEventArgs = MQTTnet.Client.Disconnecting.MqttClientDisconnectedEventArgs; namespace MQTTnet.Extensions.ManagedClient { @@ -27,7 +28,7 @@ namespace MQTTnet.Extensions.ManagedClient private CancellationTokenSource _publishingCancellationToken; private ManagedMqttClientStorageManager _storageManager; - + private bool _subscriptionsNotPushed; public ManagedMqttClient(IMqttClient mqttClient, IMqttNetChildLogger logger) @@ -102,11 +103,12 @@ namespace MQTTnet.Extensions.ManagedClient return Task.FromResult(0); } - public Task PublishAsync(MqttApplicationMessage applicationMessage) + public async Task PublishAsync(MqttApplicationMessage applicationMessage) { if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage)); - return PublishAsync(new ManagedMqttApplicationMessageBuilder().WithApplicationMessage(applicationMessage).Build()); + await PublishAsync(new ManagedMqttApplicationMessageBuilder().WithApplicationMessage(applicationMessage).Build()).ConfigureAwait(false); + return new MqttClientPublishResult(); } public async Task PublishAsync(ManagedMqttApplicationMessage applicationMessage) @@ -206,7 +208,15 @@ namespace MQTTnet.Extensions.ManagedClient } finally { - await _mqttClient.DisconnectAsync().ConfigureAwait(false); + try + { + await _mqttClient.DisconnectAsync().ConfigureAwait(false); + } + catch (Exception exception) + { + _logger.Error(exception, "Error while disconnecting."); + } + _logger.Info("Stopped"); } } @@ -345,7 +355,7 @@ namespace MQTTnet.Extensions.ManagedClient lock (_subscriptions) { - subscriptions = _subscriptions.Select(i => new TopicFilter(i.Key, i.Value)).ToList(); + subscriptions = _subscriptions.Select(i => new TopicFilter { Topic = i.Key, QualityOfServiceLevel = i.Value }).ToList(); unsubscriptions = new HashSet(_unsubscriptions); _unsubscriptions.Clear(); diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptions.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptions.cs index cba801b..537e1bb 100644 --- a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptions.cs +++ b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptions.cs @@ -1,5 +1,6 @@ using System; using MQTTnet.Client; +using MQTTnet.Client.Options; using MQTTnet.Server; namespace MQTTnet.Extensions.ManagedClient diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptionsBuilder.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptionsBuilder.cs index 3ea5b05..f58453c 100644 --- a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptionsBuilder.cs +++ b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptionsBuilder.cs @@ -1,5 +1,6 @@ using System; using MQTTnet.Client; +using MQTTnet.Client.Options; using MQTTnet.Server; namespace MQTTnet.Extensions.ManagedClient diff --git a/Source/MQTTnet/Adapter/IMqttChannelAdapter.cs b/Source/MQTTnet/Adapter/IMqttChannelAdapter.cs index 183badb..6310664 100644 --- a/Source/MQTTnet/Adapter/IMqttChannelAdapter.cs +++ b/Source/MQTTnet/Adapter/IMqttChannelAdapter.cs @@ -11,7 +11,7 @@ namespace MQTTnet.Adapter string Endpoint { get; } MqttPacketFormatterAdapter PacketFormatterAdapter { get; } - + event EventHandler ReadingPacketStarted; event EventHandler ReadingPacketCompleted; diff --git a/Source/MQTTnet/Client/IMqttClientAdapterFactory.cs b/Source/MQTTnet/Adapter/IMqttClientAdapterFactory.cs similarity index 76% rename from Source/MQTTnet/Client/IMqttClientAdapterFactory.cs rename to Source/MQTTnet/Adapter/IMqttClientAdapterFactory.cs index 197858b..cb0988d 100644 --- a/Source/MQTTnet/Client/IMqttClientAdapterFactory.cs +++ b/Source/MQTTnet/Adapter/IMqttClientAdapterFactory.cs @@ -1,7 +1,7 @@ -using MQTTnet.Adapter; +using MQTTnet.Client.Options; using MQTTnet.Diagnostics; -namespace MQTTnet.Client +namespace MQTTnet.Adapter { public interface IMqttClientAdapterFactory { diff --git a/Source/MQTTnet/Adapter/MqttChannelAdapter.cs b/Source/MQTTnet/Adapter/MqttChannelAdapter.cs index 340a528..44ef5be 100644 --- a/Source/MQTTnet/Adapter/MqttChannelAdapter.cs +++ b/Source/MQTTnet/Adapter/MqttChannelAdapter.cs @@ -16,7 +16,7 @@ namespace MQTTnet.Adapter public class MqttChannelAdapter : IMqttChannelAdapter { private const uint ErrorOperationAborted = 0x800703E3; - private const uint ReadBufferSize = 4096; // TODO: Move buffer size to config + private const int ReadBufferSize = 4096; // TODO: Move buffer size to config private readonly SemaphoreSlim _writerSemaphore = new SemaphoreSlim(1, 1); @@ -102,7 +102,7 @@ namespace MQTTnet.Adapter await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false); PacketFormatterAdapter.FreeBuffer(); - _logger.Verbose("TX >>> {0}", packet); + _logger.Verbose("TX ({0} bytes) >>> {1}", packetData.Count, packet); } catch (Exception exception) { @@ -152,7 +152,7 @@ namespace MQTTnet.Adapter throw new MqttProtocolViolationException("Received malformed packet."); } - _logger.Verbose("RX <<< {0}", packet); + _logger.Verbose("RX ({0} bytes) <<< {1}", receivedMqttPacket.TotalLength, packet); return packet; } @@ -182,7 +182,7 @@ namespace MQTTnet.Adapter if (fixedHeader.RemainingLength == 0) { - return new ReceivedMqttPacket(fixedHeader.Flags, null); + return new ReceivedMqttPacket(fixedHeader.Flags, null, 2); } var body = new byte[fixedHeader.RemainingLength]; @@ -194,15 +194,15 @@ namespace MQTTnet.Adapter var bytesLeft = body.Length - bodyOffset; if (chunkSize > bytesLeft) { - chunkSize = (uint)bytesLeft; + chunkSize = bytesLeft; } #if WINDOWS_UWP var readBytes = await _channel.ReadAsync(body, bodyOffset, (int)chunkSize, cancellationToken).ConfigureAwait(false); #else - // async/await is not used to avoid the overhead of context switches. We assume that the reamining data + // async/await is not used to avoid the overhead of context switches. We assume that the remaining data // has been sent from the sender directly after the initial bytes. - var readBytes = _channel.ReadAsync(body, bodyOffset, (int)chunkSize, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult(); + var readBytes = _channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult(); #endif cancellationToken.ThrowIfCancellationRequested(); @@ -211,7 +211,8 @@ namespace MQTTnet.Adapter bodyOffset += readBytes; } while (bodyOffset < body.Length); - return new ReceivedMqttPacket(fixedHeader.Flags, new MqttPacketBodyReader(body, 0, body.Length)); + var bodyReader = new MqttPacketBodyReader(body, 0, body.Length); + return new ReceivedMqttPacket(fixedHeader.Flags, bodyReader, fixedHeader.TotalLength); } finally { diff --git a/Source/MQTTnet/Adapter/MqttConnectingFailedException.cs b/Source/MQTTnet/Adapter/MqttConnectingFailedException.cs index 68445cf..44d50ec 100644 --- a/Source/MQTTnet/Adapter/MqttConnectingFailedException.cs +++ b/Source/MQTTnet/Adapter/MqttConnectingFailedException.cs @@ -1,16 +1,16 @@ -using MQTTnet.Exceptions; -using MQTTnet.Protocol; +using MQTTnet.Client.Connecting; +using MQTTnet.Exceptions; namespace MQTTnet.Adapter { public class MqttConnectingFailedException : MqttCommunicationException { - public MqttConnectingFailedException(MqttConnectReturnCode returnCode) - : base($"Connecting with MQTT server failed ({returnCode}).") + public MqttConnectingFailedException(MqttClientConnectResultCode resultCode) + : base($"Connecting with MQTT server failed ({resultCode.ToString()}).") { - ReturnCode = returnCode; + ResultCode = resultCode; } - public MqttConnectReturnCode ReturnCode { get; } + public MqttClientConnectResultCode ResultCode { get; } } } diff --git a/Source/MQTTnet/Adapter/ReceivedMqttPacket.cs b/Source/MQTTnet/Adapter/ReceivedMqttPacket.cs index 007b9eb..683d6b6 100644 --- a/Source/MQTTnet/Adapter/ReceivedMqttPacket.cs +++ b/Source/MQTTnet/Adapter/ReceivedMqttPacket.cs @@ -4,14 +4,17 @@ namespace MQTTnet.Adapter { public class ReceivedMqttPacket { - public ReceivedMqttPacket(byte fixedHeader, MqttPacketBodyReader body) + public ReceivedMqttPacket(byte fixedHeader, MqttPacketBodyReader body, int totalLength) { FixedHeader = fixedHeader; Body = body; + TotalLength = totalLength; } public byte FixedHeader { get; } public MqttPacketBodyReader Body { get; } + + public int TotalLength { get; } } } diff --git a/Source/MQTTnet/ApplicationMessagePublisherExtensions.cs b/Source/MQTTnet/ApplicationMessagePublisherExtensions.cs index 81dddbf..ea0b83b 100644 --- a/Source/MQTTnet/ApplicationMessagePublisherExtensions.cs +++ b/Source/MQTTnet/ApplicationMessagePublisherExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using MQTTnet.Client.Publishing; using MQTTnet.Protocol; namespace MQTTnet @@ -29,7 +30,7 @@ namespace MQTTnet } } - public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic) + public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic) { if (publisher == null) throw new ArgumentNullException(nameof(publisher)); if (topic == null) throw new ArgumentNullException(nameof(topic)); @@ -38,7 +39,7 @@ namespace MQTTnet .WithTopic(topic)); } - public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload) + public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload) { if (publisher == null) throw new ArgumentNullException(nameof(publisher)); if (topic == null) throw new ArgumentNullException(nameof(topic)); @@ -48,7 +49,7 @@ namespace MQTTnet .WithPayload(payload)); } - public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel) + public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel) { if (publisher == null) throw new ArgumentNullException(nameof(publisher)); if (topic == null) throw new ArgumentNullException(nameof(topic)); @@ -59,7 +60,7 @@ namespace MQTTnet .WithQualityOfServiceLevel(qualityOfServiceLevel)); } - public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain) + public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain) { if (publisher == null) throw new ArgumentNullException(nameof(publisher)); if (topic == null) throw new ArgumentNullException(nameof(topic)); @@ -71,7 +72,7 @@ namespace MQTTnet .WithRetainFlag(retain)); } - public static Task PublishAsync(this IApplicationMessagePublisher publisher, Func builder) + public static Task PublishAsync(this IApplicationMessagePublisher publisher, Func builder) { var message = builder(new MqttApplicationMessageBuilder()).Build(); return publisher.PublishAsync(message); diff --git a/Source/MQTTnet/Client/Connecting/MqttClientConnectResult.cs b/Source/MQTTnet/Client/Connecting/MqttClientConnectResult.cs new file mode 100644 index 0000000..05f53f0 --- /dev/null +++ b/Source/MQTTnet/Client/Connecting/MqttClientConnectResult.cs @@ -0,0 +1,9 @@ +namespace MQTTnet.Client.Connecting +{ + public class MqttClientConnectResult + { + public bool IsSessionPresent { get; set; } + + public MqttClientConnectResultCode ResultCode { get; set; } + } +} diff --git a/Source/MQTTnet/Client/Connecting/MqttClientConnectResultCode.cs b/Source/MQTTnet/Client/Connecting/MqttClientConnectResultCode.cs new file mode 100644 index 0000000..a0dbae7 --- /dev/null +++ b/Source/MQTTnet/Client/Connecting/MqttClientConnectResultCode.cs @@ -0,0 +1,28 @@ +namespace MQTTnet.Client.Connecting +{ + public enum MqttClientConnectResultCode + { + Success = 0, + UnspecifiedError = 128, + MalformedPacket = 129, + ProtocolError = 130, + ImplementationSpecificError = 131, + UnsupportedProtocolVersion = 132, + ClientIdentifierNotValid = 133, + BadUserNameOrPassword = 134, + NotAuthorized = 135, + ServerUnavailable = 136, + ServerBusy = 137, + Banned = 138, + BadAuthenticationMethod = 140, + TopicNameInvalid = 144, + PacketTooLarge = 149, + QuotaExceeded = 151, + PayloadFormatInvalid = 153, + RetainNotSupported = 154, + QoSNotSupported = 155, + UseAnotherServer = 156, + ServerMoved = 157, + ConnectionRateExceeded = 159 + } +} diff --git a/Source/MQTTnet/Client/Connecting/MqttClientConnectedEventArgs.cs b/Source/MQTTnet/Client/Connecting/MqttClientConnectedEventArgs.cs new file mode 100644 index 0000000..c052986 --- /dev/null +++ b/Source/MQTTnet/Client/Connecting/MqttClientConnectedEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace MQTTnet.Client.Connecting +{ + public class MqttClientConnectedEventArgs : EventArgs + { + public MqttClientConnectedEventArgs(MqttClientConnectResult connectResult) + { + ConnectResult = connectResult ?? throw new ArgumentNullException(nameof(connectResult)); + } + + public MqttClientConnectResult ConnectResult { get; } + } +} diff --git a/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectOptions.cs b/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectOptions.cs new file mode 100644 index 0000000..995806e --- /dev/null +++ b/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectOptions.cs @@ -0,0 +1,9 @@ +namespace MQTTnet.Client.Disconnecting +{ + public class MqttClientDisconnectOptions + { + public MqttClientDisconnectReason ReasonCode { get; set; } = MqttClientDisconnectReason.NormalDisconnection; + + public string ReasonString { get; set; } + } +} diff --git a/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectReason.cs b/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectReason.cs new file mode 100644 index 0000000..3e1bb66 --- /dev/null +++ b/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectReason.cs @@ -0,0 +1,8 @@ +namespace MQTTnet.Client.Disconnecting +{ + public enum MqttClientDisconnectReason + { + NormalDisconnection = 0, + + } +} diff --git a/Source/MQTTnet/Client/MqttClientDisconnectedEventArgs.cs b/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectedEventArgs.cs similarity index 90% rename from Source/MQTTnet/Client/MqttClientDisconnectedEventArgs.cs rename to Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectedEventArgs.cs index 2161cc2..7506042 100644 --- a/Source/MQTTnet/Client/MqttClientDisconnectedEventArgs.cs +++ b/Source/MQTTnet/Client/Disconnecting/MqttClientDisconnectedEventArgs.cs @@ -1,6 +1,6 @@ using System; -namespace MQTTnet.Client +namespace MQTTnet.Client.Disconnecting { public class MqttClientDisconnectedEventArgs : EventArgs { diff --git a/Source/MQTTnet/Client/IMqttClient.cs b/Source/MQTTnet/Client/IMqttClient.cs index b60a31a..4e9cbc1 100644 --- a/Source/MQTTnet/Client/IMqttClient.cs +++ b/Source/MQTTnet/Client/IMqttClient.cs @@ -1,6 +1,11 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using MQTTnet.Client.Connecting; +using MQTTnet.Client.Disconnecting; +using MQTTnet.Client.Options; +using MQTTnet.Client.Subscribing; +using MQTTnet.Client.Unsubscribing; namespace MQTTnet.Client { @@ -13,9 +18,9 @@ namespace MQTTnet.Client event EventHandler Disconnected; Task ConnectAsync(IMqttClientOptions options); - Task DisconnectAsync(); + Task DisconnectAsync(MqttClientDisconnectOptions options); - Task> SubscribeAsync(IEnumerable topicFilters); - Task UnsubscribeAsync(IEnumerable topics); + Task SubscribeAsync(IEnumerable topicFilters); + Task UnsubscribeAsync(IEnumerable topics); } } \ No newline at end of file diff --git a/Source/MQTTnet/Client/IMqttClientFactory.cs b/Source/MQTTnet/Client/IMqttClientFactory.cs index cd687af..95d567e 100644 --- a/Source/MQTTnet/Client/IMqttClientFactory.cs +++ b/Source/MQTTnet/Client/IMqttClientFactory.cs @@ -1,4 +1,5 @@ -using MQTTnet.Diagnostics; +using MQTTnet.Adapter; +using MQTTnet.Diagnostics; namespace MQTTnet.Client { diff --git a/Source/MQTTnet/Client/MqttClient.cs b/Source/MQTTnet/Client/MqttClient.cs index 9b0fcc5..fd4b820 100644 --- a/Source/MQTTnet/Client/MqttClient.cs +++ b/Source/MQTTnet/Client/MqttClient.cs @@ -2,14 +2,19 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Net; using System.Threading; using System.Threading.Tasks; using MQTTnet.Adapter; +using MQTTnet.Client.Connecting; +using MQTTnet.Client.Disconnecting; +using MQTTnet.Client.Options; +using MQTTnet.Client.PacketDispatcher; +using MQTTnet.Client.Publishing; +using MQTTnet.Client.Subscribing; +using MQTTnet.Client.Unsubscribing; using MQTTnet.Diagnostics; using MQTTnet.Exceptions; using MQTTnet.Formatter; -using MQTTnet.Internal; using MQTTnet.Packets; using MQTTnet.Protocol; @@ -71,7 +76,7 @@ namespace MQTTnet.Client StartReceivingPackets(_cancellationTokenSource.Token); - var connectResponse = await AuthenticateAsync(options.WillMessage, _cancellationTokenSource.Token).ConfigureAwait(false); + var connectResult = await AuthenticateAsync(options.WillMessage, _cancellationTokenSource.Token).ConfigureAwait(false); _logger.Verbose("MQTT connection with server established."); _sendTracker.Restart(); @@ -82,10 +87,11 @@ namespace MQTTnet.Client } IsConnected = true; - Connected?.Invoke(this, new MqttClientConnectedEventArgs(connectResponse.IsSessionPresent)); + Connected?.Invoke(this, new MqttClientConnectedEventArgs(connectResult)); _logger.Info("Connected."); - return new MqttClientConnectResult(connectResponse.IsSessionPresent); + + return connectResult; } catch (Exception exception) { @@ -100,7 +106,7 @@ namespace MQTTnet.Client } } - public async Task DisconnectAsync() + public async Task DisconnectAsync(MqttClientDisconnectOptions options) { try { @@ -108,7 +114,8 @@ namespace MQTTnet.Client if (IsConnected && !_cancellationTokenSource.IsCancellationRequested) { - await SendAsync(new MqttDisconnectPacket(), _cancellationTokenSource.Token).ConfigureAwait(false); + var disconnectPacket = CreateDisconnectPacket(options); + await SendAsync(disconnectPacket, _cancellationTokenSource.Token).ConfigureAwait(false); } } finally @@ -120,7 +127,7 @@ namespace MQTTnet.Client } } - public async Task> SubscribeAsync(IEnumerable topicFilters) + public async Task SubscribeAsync(IEnumerable topicFilters) { if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters)); @@ -128,21 +135,16 @@ namespace MQTTnet.Client var subscribePacket = new MqttSubscribePacket { - PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier(), - TopicFilters = topicFilters.ToList() + PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier() }; - var response = await SendAndReceiveAsync(subscribePacket, _cancellationTokenSource.Token).ConfigureAwait(false); - - if (response.SubscribeReturnCodes.Count != subscribePacket.TopicFilters.Count) - { - throw new MqttProtocolViolationException("The return codes are not matching the topic filters [MQTT-3.9.3-1]."); - } + subscribePacket.TopicFilters.AddRange(topicFilters); - return subscribePacket.TopicFilters.Select((t, i) => new MqttSubscribeResult(t, response.SubscribeReturnCodes[i])).ToList(); + var subAckPacket = await SendAndReceiveAsync(subscribePacket, _cancellationTokenSource.Token).ConfigureAwait(false); + return _adapter.PacketFormatterAdapter.DataConverter.CreateClientSubscribeResult(subscribePacket, subAckPacket); } - public Task UnsubscribeAsync(IEnumerable topicFilters) + public async Task UnsubscribeAsync(IEnumerable topicFilters) { if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters)); @@ -150,36 +152,47 @@ namespace MQTTnet.Client var unsubscribePacket = new MqttUnsubscribePacket { - PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier(), - TopicFilters = topicFilters.ToList() + PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier() }; - return SendAndReceiveAsync(unsubscribePacket, _cancellationTokenSource.Token); + unsubscribePacket.TopicFilters.AddRange(topicFilters); + + var unsubAckPacket = await SendAndReceiveAsync(unsubscribePacket, _cancellationTokenSource.Token).ConfigureAwait(false); + return _adapter.PacketFormatterAdapter.DataConverter.CreateClientUnsubscribeResult(unsubscribePacket, unsubAckPacket); } - public Task PublishAsync(MqttApplicationMessage applicationMessage) + public async Task PublishAsync(MqttApplicationMessage applicationMessage) { if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage)); ThrowIfNotConnected(); - var publishPacket = _adapter.PacketFormatterAdapter.ConvertApplicationMessageToPublishPacket(applicationMessage); + var publishPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePublishPacket(applicationMessage); switch (applicationMessage.QualityOfServiceLevel) { case MqttQualityOfServiceLevel.AtMostOnce: { // No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier] - return SendAsync(publishPacket, _cancellationTokenSource.Token); + await SendAsync(publishPacket, _cancellationTokenSource.Token).ConfigureAwait(false); + return new MqttClientPublishResult(); } case MqttQualityOfServiceLevel.AtLeastOnce: { publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier(); - return SendAndReceiveAsync(publishPacket, _cancellationTokenSource.Token); + var response = await SendAndReceiveAsync(publishPacket, _cancellationTokenSource.Token).ConfigureAwait(false); + + var result = new MqttClientPublishResult(); + if (response.ReasonCode != null) + { + result.ReasonCode = (MqttClientPublishReasonCode)response.ReasonCode; + } + + return result; } case MqttQualityOfServiceLevel.ExactlyOnce: { - return PublishExactlyOnce(publishPacket, _cancellationTokenSource.Token); + return await PublishExactlyOnceAsync(publishPacket, _cancellationTokenSource.Token).ConfigureAwait(false); } default: { @@ -198,28 +211,22 @@ namespace MQTTnet.Client _adapter = null; } - private async Task AuthenticateAsync(MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken) + private async Task AuthenticateAsync(MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken) { - var connectPacket = new MqttConnectPacket - { - ClientId = Options.ClientId, - Username = Options.Credentials?.Username, - Password = Options.Credentials?.Password, - CleanSession = Options.CleanSession, - KeepAlivePeriod = (ushort)Options.KeepAlivePeriod.TotalSeconds, - WillMessage = willApplicationMessage - }; + var connectPacket = _adapter.PacketFormatterAdapter.DataConverter.CreateConnectPacket( + willApplicationMessage, + Options); - //connectPacket.RequestProblemInformationProperty = true; - //connectPacket.RequestResponseInformationProperty = true; + var connAckPacket = await SendAndReceiveAsync(connectPacket, cancellationToken).ConfigureAwait(false); - var response = await SendAndReceiveAsync(connectPacket, cancellationToken).ConfigureAwait(false); - if (response.ConnectReturnCode != MqttConnectReturnCode.ConnectionAccepted) + var result = _adapter.PacketFormatterAdapter.DataConverter.CreateClientConnectResult(connAckPacket); + + if (result.ResultCode != MqttClientConnectResultCode.Success) { - throw new MqttConnectingFailedException(response.ConnectReturnCode); + throw new MqttConnectingFailedException(result.ResultCode); } - return response; + return result; } private void ThrowIfNotConnected() @@ -237,7 +244,7 @@ namespace MQTTnet.Client var clientWasConnected = IsConnected; InitiateDisconnect(); - + IsConnected = false; try @@ -249,7 +256,7 @@ namespace MQTTnet.Client await WaitForTaskAsync(_packetReceiverTask, sender).ConfigureAwait(false); await WaitForTaskAsync(_keepAliveMessageSenderTask, sender).ConfigureAwait(false); - + _logger.Verbose("Disconnected from adapter."); } catch (Exception adapterException) @@ -311,9 +318,9 @@ namespace MQTTnet.Client try { await _adapter.SendPacketAsync(requestPacket, cancellationToken).ConfigureAwait(false); - var respone = await Internal.TaskExtensions.TimeoutAfterAsync(ct => packetAwaiter.Task, Options.CommunicationTimeout, cancellationToken).ConfigureAwait(false); + return await packetAwaiter.WaitOneAsync(Options.CommunicationTimeout); - return (TResponsePacket)respone; + //return (TResponsePacket)await Internal.TaskExtensions.TimeoutAfterAsync(ct => packetAwaiter.Task, Options.CommunicationTimeout, cancellationToken).ConfigureAwait(false); } catch (MqttCommunicationTimedOutException) { @@ -388,8 +395,7 @@ namespace MQTTnet.Client while (!cancellationToken.IsCancellationRequested) { - var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken) - .ConfigureAwait(false); + var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false); if (packet != null && !cancellationToken.IsCancellationRequested) { @@ -443,7 +449,7 @@ namespace MQTTnet.Client if (packet is MqttDisconnectPacket) { - return DisconnectAsync(); + return DisconnectAsync(null); } if (packet is MqttPubRelPacket pubRelPacket) @@ -466,14 +472,14 @@ namespace MQTTnet.Client if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce) { FireApplicationMessageReceivedEvent(publishPacket); - return SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier }, cancellationToken); + return SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier, ReasonCode = MqttPubAckReasonCode.Success }, cancellationToken); } if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce) { // QoS 2 is implement as method "B" (4.3.3 QoS 2: Exactly once delivery) FireApplicationMessageReceivedEvent(publishPacket); - return SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }, cancellationToken); + return SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier, ReasonCode = MqttPubRecReasonCode.Success }, cancellationToken); } throw new MqttCommunicationException("Received a not supported QoS level."); @@ -483,23 +489,33 @@ namespace MQTTnet.Client { var response = new MqttPubCompPacket { - PacketIdentifier = pubRelPacket.PacketIdentifier + PacketIdentifier = pubRelPacket.PacketIdentifier, + ReasonCode = MqttPubCompReasonCode.Success }; return SendAsync(response, cancellationToken); } - private async Task PublishExactlyOnce(MqttPublishPacket publishPacket, CancellationToken cancellationToken) + private async Task PublishExactlyOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken) { publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier(); var pubRecPacket = await SendAndReceiveAsync(publishPacket, cancellationToken).ConfigureAwait(false); var pubRelPacket = new MqttPubRelPacket { - PacketIdentifier = pubRecPacket.PacketIdentifier + PacketIdentifier = pubRecPacket.PacketIdentifier, + ReasonCode = MqttPubRelReasonCode.Success }; - await SendAndReceiveAsync(pubRelPacket, cancellationToken).ConfigureAwait(false); + var pubCompPacket = await SendAndReceiveAsync(pubRelPacket, cancellationToken).ConfigureAwait(false); + var result = new MqttClientPublishResult(); + + if (pubRecPacket.ReasonCode != null) + { + result.ReasonCode = (MqttClientPublishReasonCode)pubRecPacket.ReasonCode; + } + + return result; } private void StartReceivingPackets(CancellationToken cancellationToken) @@ -507,7 +523,7 @@ namespace MQTTnet.Client _packetReceiverTask = Task.Factory.StartNew( () => ReceivePacketsAsync(cancellationToken), cancellationToken, - TaskCreationOptions.LongRunning, + TaskCreationOptions.LongRunning, TaskScheduler.Default).Unwrap(); } @@ -524,8 +540,7 @@ namespace MQTTnet.Client { try { - // TODO: Move conversion to formatter. - var applicationMessage = publishPacket.ToApplicationMessage(); + var applicationMessage = _adapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket); ApplicationMessageReceived?.Invoke(this, new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage)); } catch (Exception exception) @@ -559,5 +574,31 @@ namespace MQTTnet.Client { return Interlocked.CompareExchange(ref _disconnectGate, 1, 0) != 0; } + + private MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options) + { + var packet = new MqttDisconnectPacket(); + + if (_adapter.PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.V500) + { + if (options == null) + { + packet.ReasonCode = MqttDisconnectReasonCode.NormalDisconnection; + } + else + { + packet.ReasonCode = (MqttDisconnectReasonCode)options.ReasonCode; + } + } + else + { + if (options != null) + { + throw new MqttProtocolViolationException("Reason codes for disconnect are only supported for MQTTv5."); + } + } + + return packet; + } } } diff --git a/Source/MQTTnet/Client/MqttClientConnectResult.cs b/Source/MQTTnet/Client/MqttClientConnectResult.cs deleted file mode 100644 index 6044bf5..0000000 --- a/Source/MQTTnet/Client/MqttClientConnectResult.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace MQTTnet.Client -{ - public class MqttClientConnectResult - { - public MqttClientConnectResult(bool isSessionPresent) - { - IsSessionPresent = isSessionPresent; - } - - public bool IsSessionPresent { get; } - } -} diff --git a/Source/MQTTnet/Client/MqttClientConnectedEventArgs.cs b/Source/MQTTnet/Client/MqttClientConnectedEventArgs.cs deleted file mode 100644 index c8c6ce5..0000000 --- a/Source/MQTTnet/Client/MqttClientConnectedEventArgs.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace MQTTnet.Client -{ - public class MqttClientConnectedEventArgs : EventArgs - { - public MqttClientConnectedEventArgs(bool isSessionPresent) - { - IsSessionPresent = isSessionPresent; - } - - public bool IsSessionPresent { get; } - } -} diff --git a/Source/MQTTnet/Client/MqttClientExtensions.cs b/Source/MQTTnet/Client/MqttClientExtensions.cs index a7548de..00482ea 100644 --- a/Source/MQTTnet/Client/MqttClientExtensions.cs +++ b/Source/MQTTnet/Client/MqttClientExtensions.cs @@ -1,14 +1,22 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using MQTTnet.Client.Subscribing; +using MQTTnet.Client.Unsubscribing; using MQTTnet.Protocol; namespace MQTTnet.Client { public static class MqttClientExtensions { - public static Task> SubscribeAsync(this IMqttClient client, params TopicFilter[] topicFilters) + public static Task DisconnectAsync(this IMqttClient client) + { + if (client == null) throw new ArgumentNullException(nameof(client)); + + return client.DisconnectAsync(null); + } + + public static Task SubscribeAsync(this IMqttClient client, params TopicFilter[] topicFilters) { if (client == null) throw new ArgumentNullException(nameof(client)); if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters)); @@ -16,7 +24,7 @@ namespace MQTTnet.Client return client.SubscribeAsync(topicFilters.ToList()); } - public static Task> SubscribeAsync(this IMqttClient client, string topic, MqttQualityOfServiceLevel qualityOfServiceLevel) + public static Task SubscribeAsync(this IMqttClient client, string topic, MqttQualityOfServiceLevel qualityOfServiceLevel) { if (client == null) throw new ArgumentNullException(nameof(client)); if (topic == null) throw new ArgumentNullException(nameof(topic)); @@ -24,7 +32,7 @@ namespace MQTTnet.Client return client.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build()); } - public static Task> SubscribeAsync(this IMqttClient client, string topic) + public static Task SubscribeAsync(this IMqttClient client, string topic) { if (client == null) throw new ArgumentNullException(nameof(client)); if (topic == null) throw new ArgumentNullException(nameof(topic)); @@ -32,7 +40,7 @@ namespace MQTTnet.Client return client.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).Build()); } - public static Task UnsubscribeAsync(this IMqttClient client, params string[] topicFilters) + public static Task UnsubscribeAsync(this IMqttClient client, params string[] topicFilters) { if (client == null) throw new ArgumentNullException(nameof(client)); if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters)); diff --git a/Source/MQTTnet/Client/MqttSubscribeResult.cs b/Source/MQTTnet/Client/MqttSubscribeResult.cs deleted file mode 100644 index 2b7c46d..0000000 --- a/Source/MQTTnet/Client/MqttSubscribeResult.cs +++ /dev/null @@ -1,17 +0,0 @@ -using MQTTnet.Protocol; - -namespace MQTTnet.Client -{ - public class MqttSubscribeResult - { - public MqttSubscribeResult(TopicFilter topicFilter, MqttSubscribeReturnCode returnCode) - { - TopicFilter = topicFilter; - ReturnCode = returnCode; - } - - public TopicFilter TopicFilter { get; } - - public MqttSubscribeReturnCode ReturnCode { get; } - } -} diff --git a/Source/MQTTnet/Client/IMqttClientChannelOptions.cs b/Source/MQTTnet/Client/Options/IMqttClientChannelOptions.cs similarity index 75% rename from Source/MQTTnet/Client/IMqttClientChannelOptions.cs rename to Source/MQTTnet/Client/Options/IMqttClientChannelOptions.cs index 2224963..729d12f 100644 --- a/Source/MQTTnet/Client/IMqttClientChannelOptions.cs +++ b/Source/MQTTnet/Client/Options/IMqttClientChannelOptions.cs @@ -1,4 +1,4 @@ -namespace MQTTnet.Client +namespace MQTTnet.Client.Options { public interface IMqttClientChannelOptions { diff --git a/Source/MQTTnet/Client/IMqttClientCredentials.cs b/Source/MQTTnet/Client/Options/IMqttClientCredentials.cs similarity index 77% rename from Source/MQTTnet/Client/IMqttClientCredentials.cs rename to Source/MQTTnet/Client/Options/IMqttClientCredentials.cs index d214d64..ec18867 100644 --- a/Source/MQTTnet/Client/IMqttClientCredentials.cs +++ b/Source/MQTTnet/Client/Options/IMqttClientCredentials.cs @@ -1,4 +1,4 @@ -namespace MQTTnet.Client +namespace MQTTnet.Client.Options { public interface IMqttClientCredentials { diff --git a/Source/MQTTnet/Client/IMqttClientOptions.cs b/Source/MQTTnet/Client/Options/IMqttClientOptions.cs similarity index 55% rename from Source/MQTTnet/Client/IMqttClientOptions.cs rename to Source/MQTTnet/Client/Options/IMqttClientOptions.cs index 8825a64..3150baa 100644 --- a/Source/MQTTnet/Client/IMqttClientOptions.cs +++ b/Source/MQTTnet/Client/Options/IMqttClientOptions.cs @@ -1,7 +1,7 @@ using System; using MQTTnet.Formatter; -namespace MQTTnet.Client +namespace MQTTnet.Client.Options { public interface IMqttClientOptions { @@ -16,5 +16,15 @@ namespace MQTTnet.Client TimeSpan? KeepAliveSendInterval { get; } MqttApplicationMessage WillMessage { get; } + uint? WillDelayInterval { get; } + + string AuthenticationMethod { get; } + byte[] AuthenticationData { get; } + uint? MaximumPacketSize { get; } + ushort? ReceiveMaximum { get; } + bool? RequestProblemInformation { get; } + bool? RequestResponseInformation { get; } + uint? SessionExpiryInterval { get; } + ushort? TopicAliasMaximum { get; } } } \ No newline at end of file diff --git a/Source/MQTTnet/Client/MqttClientCredentials.cs b/Source/MQTTnet/Client/Options/MqttClientCredentials.cs similarity index 82% rename from Source/MQTTnet/Client/MqttClientCredentials.cs rename to Source/MQTTnet/Client/Options/MqttClientCredentials.cs index feaa1e6..8836e36 100644 --- a/Source/MQTTnet/Client/MqttClientCredentials.cs +++ b/Source/MQTTnet/Client/Options/MqttClientCredentials.cs @@ -1,4 +1,4 @@ -namespace MQTTnet.Client +namespace MQTTnet.Client.Options { public class MqttClientCredentials : IMqttClientCredentials { diff --git a/Source/MQTTnet/Client/MqttClientOptions.cs b/Source/MQTTnet/Client/Options/MqttClientOptions.cs similarity index 59% rename from Source/MQTTnet/Client/MqttClientOptions.cs rename to Source/MQTTnet/Client/Options/MqttClientOptions.cs index 1e5182c..a76ecf5 100644 --- a/Source/MQTTnet/Client/MqttClientOptions.cs +++ b/Source/MQTTnet/Client/Options/MqttClientOptions.cs @@ -1,7 +1,7 @@ using System; using MQTTnet.Formatter; -namespace MQTTnet.Client +namespace MQTTnet.Client.Options { public class MqttClientOptions : IMqttClientOptions { @@ -9,12 +9,23 @@ namespace MQTTnet.Client public bool CleanSession { get; set; } = true; public IMqttClientCredentials Credentials { get; set; } = new MqttClientCredentials(); public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311; - public IMqttClientChannelOptions ChannelOptions { get; set; } + public IMqttClientChannelOptions ChannelOptions { get; set; } public TimeSpan CommunicationTimeout { get; set; } = TimeSpan.FromSeconds(10); public TimeSpan KeepAlivePeriod { get; set; } = TimeSpan.FromSeconds(15); public TimeSpan? KeepAliveSendInterval { get; set; } public MqttApplicationMessage WillMessage { get; set; } + public uint? WillDelayInterval { get; set; } + + public string AuthenticationMethod { get; set; } + public byte[] AuthenticationData { get; set; } + + public uint? MaximumPacketSize { get; set; } + public ushort? ReceiveMaximum { get; set; } + public bool? RequestProblemInformation { get; set; } + public bool? RequestResponseInformation { get; set; } + public uint? SessionExpiryInterval { get; set; } + public ushort? TopicAliasMaximum { get; set; } } } diff --git a/Source/MQTTnet/Client/MqttClientOptionsBuilder.cs b/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs similarity index 78% rename from Source/MQTTnet/Client/MqttClientOptionsBuilder.cs rename to Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs index 34ecb94..6300f6d 100644 --- a/Source/MQTTnet/Client/MqttClientOptionsBuilder.cs +++ b/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs @@ -2,7 +2,7 @@ using System.Linq; using MQTTnet.Formatter; -namespace MQTTnet.Client +namespace MQTTnet.Client.Options { public class MqttClientOptionsBuilder { @@ -55,6 +55,55 @@ namespace MQTTnet.Client return this; } + public MqttClientOptionsBuilder WithAuthentication(string method, byte[] data) + { + _options.AuthenticationMethod = method; + _options.AuthenticationData = data; + return this; + } + + public MqttClientOptionsBuilder WithWillDelayInterval(uint? willDelayInterval) + { + _options.WillDelayInterval = willDelayInterval; + return this; + } + + public MqttClientOptionsBuilder WithTopicAliasMaximum(ushort? topicAliasMaximum) + { + _options.TopicAliasMaximum = topicAliasMaximum; + return this; + } + + public MqttClientOptionsBuilder WithMaximumPacketSize(uint? maximumPacketSize) + { + _options.MaximumPacketSize = maximumPacketSize; + return this; + } + + public MqttClientOptionsBuilder WithReceiveMaximum(ushort? receiveMaximum) + { + _options.ReceiveMaximum = receiveMaximum; + return this; + } + + public MqttClientOptionsBuilder WithRequestProblemInformation(bool? requestProblemInformation = true) + { + _options.RequestProblemInformation = requestProblemInformation; + return this; + } + + public MqttClientOptionsBuilder WithRequestResponseInformation(bool? requestResponseInformation = true) + { + _options.RequestResponseInformation = requestResponseInformation; + return this; + } + + public MqttClientOptionsBuilder WithSessionExpiryInterval(uint? sessionExpiryInterval) + { + _options.SessionExpiryInterval = sessionExpiryInterval; + return this; + } + public MqttClientOptionsBuilder WithCredentials(string username, string password = null) { _options.Credentials = new MqttClientCredentials diff --git a/Source/MQTTnet/Client/MqttClientOptionsBuilderTlsParameters.cs b/Source/MQTTnet/Client/Options/MqttClientOptionsBuilderTlsParameters.cs similarity index 96% rename from Source/MQTTnet/Client/MqttClientOptionsBuilderTlsParameters.cs rename to Source/MQTTnet/Client/Options/MqttClientOptionsBuilderTlsParameters.cs index 5764c79..ea36baa 100644 --- a/Source/MQTTnet/Client/MqttClientOptionsBuilderTlsParameters.cs +++ b/Source/MQTTnet/Client/Options/MqttClientOptionsBuilderTlsParameters.cs @@ -4,7 +4,7 @@ using System.Net.Security; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; -namespace MQTTnet.Client +namespace MQTTnet.Client.Options { public class MqttClientOptionsBuilderTlsParameters { diff --git a/Source/MQTTnet/Client/MqttClientTcpOptions.cs b/Source/MQTTnet/Client/Options/MqttClientTcpOptions.cs similarity index 92% rename from Source/MQTTnet/Client/MqttClientTcpOptions.cs rename to Source/MQTTnet/Client/Options/MqttClientTcpOptions.cs index 4569be3..35af34f 100644 --- a/Source/MQTTnet/Client/MqttClientTcpOptions.cs +++ b/Source/MQTTnet/Client/Options/MqttClientTcpOptions.cs @@ -1,4 +1,4 @@ -namespace MQTTnet.Client +namespace MQTTnet.Client.Options { public class MqttClientTcpOptions : IMqttClientChannelOptions { diff --git a/Source/MQTTnet/Client/MqttClientTcpOptionsExtensions.cs b/Source/MQTTnet/Client/Options/MqttClientTcpOptionsExtensions.cs similarity index 92% rename from Source/MQTTnet/Client/MqttClientTcpOptionsExtensions.cs rename to Source/MQTTnet/Client/Options/MqttClientTcpOptionsExtensions.cs index 460069a..1b49a58 100644 --- a/Source/MQTTnet/Client/MqttClientTcpOptionsExtensions.cs +++ b/Source/MQTTnet/Client/Options/MqttClientTcpOptionsExtensions.cs @@ -1,6 +1,6 @@ using System; -namespace MQTTnet.Client +namespace MQTTnet.Client.Options { public static class MqttClientTcpOptionsExtensions { diff --git a/Source/MQTTnet/Client/MqttClientTlsOptions.cs b/Source/MQTTnet/Client/Options/MqttClientTlsOptions.cs similarity index 95% rename from Source/MQTTnet/Client/MqttClientTlsOptions.cs rename to Source/MQTTnet/Client/Options/MqttClientTlsOptions.cs index 73d65cb..db4077d 100644 --- a/Source/MQTTnet/Client/MqttClientTlsOptions.cs +++ b/Source/MQTTnet/Client/Options/MqttClientTlsOptions.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.Net.Security; -using System.Security.Cryptography.X509Certificates; using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; -namespace MQTTnet.Client +namespace MQTTnet.Client.Options { public class MqttClientTlsOptions { diff --git a/Source/MQTTnet/Client/MqttClientWebSocketOptions.cs b/Source/MQTTnet/Client/Options/MqttClientWebSocketOptions.cs similarity index 95% rename from Source/MQTTnet/Client/MqttClientWebSocketOptions.cs rename to Source/MQTTnet/Client/Options/MqttClientWebSocketOptions.cs index fe8f1d2..d62f1f1 100644 --- a/Source/MQTTnet/Client/MqttClientWebSocketOptions.cs +++ b/Source/MQTTnet/Client/Options/MqttClientWebSocketOptions.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Net; -namespace MQTTnet.Client +namespace MQTTnet.Client.Options { public class MqttClientWebSocketOptions : IMqttClientChannelOptions { diff --git a/Source/MQTTnet/Client/MqttClientWebSocketProxyOptions.cs b/Source/MQTTnet/Client/Options/MqttClientWebSocketProxyOptions.cs similarity index 90% rename from Source/MQTTnet/Client/MqttClientWebSocketProxyOptions.cs rename to Source/MQTTnet/Client/Options/MqttClientWebSocketProxyOptions.cs index 13b1366..7ff40f8 100644 --- a/Source/MQTTnet/Client/MqttClientWebSocketProxyOptions.cs +++ b/Source/MQTTnet/Client/Options/MqttClientWebSocketProxyOptions.cs @@ -1,4 +1,4 @@ -namespace MQTTnet.Client +namespace MQTTnet.Client.Options { public class MqttClientWebSocketProxyOptions { diff --git a/Source/MQTTnet/Client/PacketDispatcher/IMqttPacketAwaiter.cs b/Source/MQTTnet/Client/PacketDispatcher/IMqttPacketAwaiter.cs new file mode 100644 index 0000000..4887b1e --- /dev/null +++ b/Source/MQTTnet/Client/PacketDispatcher/IMqttPacketAwaiter.cs @@ -0,0 +1,12 @@ +using System; +using MQTTnet.Packets; + +namespace MQTTnet.Client.PacketDispatcher +{ + public interface IMqttPacketAwaiter + { + void Complete(MqttBasePacket packet); + + void Fail(Exception exception); + } +} \ No newline at end of file diff --git a/Source/MQTTnet/Client/PacketDispatcher/MqttPacketAwaiter.cs b/Source/MQTTnet/Client/PacketDispatcher/MqttPacketAwaiter.cs new file mode 100644 index 0000000..292f231 --- /dev/null +++ b/Source/MQTTnet/Client/PacketDispatcher/MqttPacketAwaiter.cs @@ -0,0 +1,37 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using MQTTnet.Packets; + +namespace MQTTnet.Client.PacketDispatcher +{ + public sealed class MqttPacketAwaiter : IMqttPacketAwaiter where TPacket : MqttBasePacket + { + private readonly TaskCompletionSource _packet = new TaskCompletionSource(); + + public async Task WaitOneAsync(TimeSpan timeout) + { + using (var timeoutToken = new CancellationTokenSource(timeout)) + { + timeoutToken.Token.Register(() => _packet.TrySetCanceled()); + + var packet = await _packet.Task.ConfigureAwait(false); + return (TPacket)packet; + } + } + + public void Complete(MqttBasePacket packet) + { + if (packet == null) throw new ArgumentNullException(nameof(packet)); + + _packet.TrySetResult(packet); + } + + public void Fail(Exception exception) + { + if (exception == null) throw new ArgumentNullException(nameof(exception)); + + _packet.TrySetException(exception); + } + } +} \ No newline at end of file diff --git a/Source/MQTTnet/Client/MqttPacketDispatcher.cs b/Source/MQTTnet/Client/PacketDispatcher/MqttPacketDispatcher.cs similarity index 68% rename from Source/MQTTnet/Client/MqttPacketDispatcher.cs rename to Source/MQTTnet/Client/PacketDispatcher/MqttPacketDispatcher.cs index fd205c5..8c6a769 100644 --- a/Source/MQTTnet/Client/MqttPacketDispatcher.cs +++ b/Source/MQTTnet/Client/PacketDispatcher/MqttPacketDispatcher.cs @@ -3,17 +3,17 @@ using System.Collections.Concurrent; using System.Threading.Tasks; using MQTTnet.Packets; -namespace MQTTnet.Client +namespace MQTTnet.Client.PacketDispatcher { public class MqttPacketDispatcher { - private readonly ConcurrentDictionary, TaskCompletionSource> _awaiters = new ConcurrentDictionary, TaskCompletionSource>(); - + private readonly ConcurrentDictionary, IMqttPacketAwaiter> _awaiters = new ConcurrentDictionary, IMqttPacketAwaiter>(); + public void Dispatch(Exception exception) { foreach (var awaiter in _awaiters) { - Task.Run(() => awaiter.Value.TrySetException(exception)); // Task.Run fixes a dead lock. Without this the client only receives one message. + Task.Run(() => awaiter.Value.Fail(exception)); // Task.Run fixes a dead lock. Without this the client only receives one message. } _awaiters.Clear(); @@ -31,10 +31,10 @@ namespace MQTTnet.Client var type = packet.GetType(); var key = new Tuple(identifier, type); - + if (_awaiters.TryRemove(key, out var awaiter)) { - Task.Run(() => awaiter.TrySetResult(packet)); // Task.Run fixes a dead lock. Without this the client only receives one message. + Task.Run(() => awaiter.Complete(packet)); // Task.Run fixes a dead lock. Without this the client only receives one message. return; } @@ -46,22 +46,22 @@ namespace MQTTnet.Client _awaiters.Clear(); } - public TaskCompletionSource AddPacketAwaiter(ushort? identifier) where TResponsePacket : MqttBasePacket + public MqttPacketAwaiter AddPacketAwaiter(ushort? identifier) where TResponsePacket : MqttBasePacket { - var tcs = new TaskCompletionSource(); + var awaiter = new MqttPacketAwaiter(); if (!identifier.HasValue) { identifier = 0; } - - var key = new Tuple(identifier ?? 0, typeof(TResponsePacket)); - if (!_awaiters.TryAdd(key, tcs)) + + var key = new Tuple(identifier.Value, typeof(TResponsePacket)); + if (!_awaiters.TryAdd(key, awaiter)) { throw new InvalidOperationException($"The packet dispatcher already has an awaiter for packet of type '{key.Item2.Name}' with identifier {key.Item1}."); } - return tcs; + return awaiter; } public void RemovePacketAwaiter(ushort? identifier) where TResponsePacket : MqttBasePacket diff --git a/Source/MQTTnet/Client/Publishing/MqttClientPublishReasonCode.cs b/Source/MQTTnet/Client/Publishing/MqttClientPublishReasonCode.cs new file mode 100644 index 0000000..7b485c6 --- /dev/null +++ b/Source/MQTTnet/Client/Publishing/MqttClientPublishReasonCode.cs @@ -0,0 +1,15 @@ +namespace MQTTnet.Client.Publishing +{ + public enum MqttClientPublishReasonCode + { + Success = 0, + NoMatchingSubscribers = 16, + UnspecifiedError = 128, + ImplementationSpecificError = 131, + NotAuthorized = 135, + TopicNameInvalid = 144, + PacketIdentifierInUse = 145, + QuotaExceeded = 151, + PayloadFormatInvalid = 153 + } +} diff --git a/Source/MQTTnet/Client/Publishing/MqttClientPublishResult.cs b/Source/MQTTnet/Client/Publishing/MqttClientPublishResult.cs new file mode 100644 index 0000000..0e50813 --- /dev/null +++ b/Source/MQTTnet/Client/Publishing/MqttClientPublishResult.cs @@ -0,0 +1,8 @@ + +namespace MQTTnet.Client.Publishing +{ + public class MqttClientPublishResult + { + public MqttClientPublishReasonCode ReasonCode { get; set; } = MqttClientPublishReasonCode.Success; + } +} diff --git a/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResult.cs b/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResult.cs new file mode 100644 index 0000000..66d68d4 --- /dev/null +++ b/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResult.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace MQTTnet.Client.Subscribing +{ + public class MqttClientSubscribeResult + { + public List Items { get; } = new List(); + } +} diff --git a/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResultCode.cs b/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResultCode.cs new file mode 100644 index 0000000..119ec4f --- /dev/null +++ b/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResultCode.cs @@ -0,0 +1,18 @@ +namespace MQTTnet.Client.Subscribing +{ + public enum MqttClientSubscribeResultCode + { + GrantedQoS0 = 0, + GrantedQoS1 = 1, + GrantedQoS2 = 2, + UnspecifiedError = 128, + ImplementationSpecificError = 131, + NotAuthorized = 135, + TopicFilterInvalid = 143, + PacketIdentifierInUse = 145, + QuotaExceeded = 151, + SharedSubscriptionsNotSupported = 158, + SubscriptionIdentifiersNotSupported = 161, + WildcardSubscriptionsNotSupported = 162 + } +} diff --git a/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResultItem.cs b/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResultItem.cs new file mode 100644 index 0000000..286123f --- /dev/null +++ b/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResultItem.cs @@ -0,0 +1,17 @@ +using System; + +namespace MQTTnet.Client.Subscribing +{ + public class MqttClientSubscribeResultItem + { + public MqttClientSubscribeResultItem(TopicFilter topicFilter, MqttClientSubscribeResultCode resultCode) + { + TopicFilter = topicFilter ?? throw new ArgumentNullException(nameof(topicFilter)); + ResultCode = resultCode; + } + + public TopicFilter TopicFilter { get; } + + public MqttClientSubscribeResultCode ResultCode { get; } + } +} diff --git a/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResult.cs b/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResult.cs new file mode 100644 index 0000000..f3e4c4c --- /dev/null +++ b/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResult.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace MQTTnet.Client.Unsubscribing +{ + public class MqttClientUnsubscribeResult + { + public List Items { get; } =new List(); + } +} diff --git a/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResultCode.cs b/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResultCode.cs new file mode 100644 index 0000000..269abc1 --- /dev/null +++ b/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResultCode.cs @@ -0,0 +1,13 @@ +namespace MQTTnet.Client.Unsubscribing +{ + public enum MqttClientUnsubscribeResultCode + { + Success = 0, + NoSubscriptionExisted = 17, + UnspecifiedError = 128, + ImplementationSpecificError = 131, + NotAuthorized = 135, + TopicFilterInvalid = 143, + PacketIdentifierInUse = 145 + } +} diff --git a/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResultItem.cs b/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResultItem.cs new file mode 100644 index 0000000..755ee43 --- /dev/null +++ b/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResultItem.cs @@ -0,0 +1,17 @@ +using System; + +namespace MQTTnet.Client.Unsubscribing +{ + public class MqttClientUnsubscribeResultItem + { + public MqttClientUnsubscribeResultItem(string topicFilter, MqttClientUnsubscribeResultCode reasonCode) + { + TopicFilter = topicFilter ?? throw new ArgumentNullException(nameof(topicFilter)); + ReasonCode = reasonCode; + } + + public string TopicFilter { get; } + + public MqttClientUnsubscribeResultCode ReasonCode { get; } + } +} \ No newline at end of file diff --git a/Source/MQTTnet/Formatter/IMqttDataConverter.cs b/Source/MQTTnet/Formatter/IMqttDataConverter.cs new file mode 100644 index 0000000..44516ee --- /dev/null +++ b/Source/MQTTnet/Formatter/IMqttDataConverter.cs @@ -0,0 +1,23 @@ +using MQTTnet.Client.Connecting; +using MQTTnet.Client.Options; +using MQTTnet.Client.Subscribing; +using MQTTnet.Client.Unsubscribing; +using MQTTnet.Packets; + +namespace MQTTnet.Formatter +{ + public interface IMqttDataConverter + { + MqttPublishPacket CreatePublishPacket(MqttApplicationMessage applicationMessage); + + MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket); + + MqttClientConnectResult CreateClientConnectResult(MqttConnAckPacket connAckPacket); + + MqttConnectPacket CreateConnectPacket(MqttApplicationMessage willApplicationMessage, IMqttClientOptions options); + + MqttClientSubscribeResult CreateClientSubscribeResult(MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket); + + MqttClientUnsubscribeResult CreateClientUnsubscribeResult(MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket); + } +} diff --git a/Source/MQTTnet/Formatter/IMqttPacketFormatter.cs b/Source/MQTTnet/Formatter/IMqttPacketFormatter.cs index e37d2a7..6dd417c 100644 --- a/Source/MQTTnet/Formatter/IMqttPacketFormatter.cs +++ b/Source/MQTTnet/Formatter/IMqttPacketFormatter.cs @@ -6,12 +6,12 @@ namespace MQTTnet.Formatter { public interface IMqttPacketFormatter { + IMqttDataConverter DataConverter { get; } + ArraySegment Encode(MqttBasePacket mqttPacket); MqttBasePacket Decode(ReceivedMqttPacket receivedMqttPacket); - MqttPublishPacket ConvertApplicationMessageToPublishPacket(MqttApplicationMessage applicationMessage); - void FreeBuffer(); } } \ No newline at end of file diff --git a/Source/MQTTnet/Formatter/MqttFixedHeader.cs b/Source/MQTTnet/Formatter/MqttFixedHeader.cs index 4395d87..4c69333 100644 --- a/Source/MQTTnet/Formatter/MqttFixedHeader.cs +++ b/Source/MQTTnet/Formatter/MqttFixedHeader.cs @@ -2,14 +2,17 @@ { public struct MqttFixedHeader { - public MqttFixedHeader(byte flags, uint remainingLength) + public MqttFixedHeader(byte flags, int remainingLength, int totalLength) { Flags = flags; RemainingLength = remainingLength; + TotalLength = totalLength; } public byte Flags { get; } - public uint RemainingLength { get; } + public int RemainingLength { get; } + + public int TotalLength { get; } } } diff --git a/Source/MQTTnet/Formatter/MqttPacketFormatterAdapter.cs b/Source/MQTTnet/Formatter/MqttPacketFormatterAdapter.cs index f04467c..104318e 100644 --- a/Source/MQTTnet/Formatter/MqttPacketFormatterAdapter.cs +++ b/Source/MQTTnet/Formatter/MqttPacketFormatterAdapter.cs @@ -1,9 +1,8 @@ using System; using MQTTnet.Adapter; using MQTTnet.Exceptions; -using MQTTnet.Formatter.V310; -using MQTTnet.Formatter.V311; -using MQTTnet.Formatter.V500; +using MQTTnet.Formatter.V3; +using MQTTnet.Formatter.V5; using MQTTnet.Packets; namespace MQTTnet.Formatter @@ -22,35 +21,26 @@ namespace MQTTnet.Formatter } public MqttProtocolVersion? ProtocolVersion { get; private set; } - - public MqttPublishPacket ConvertApplicationMessageToPublishPacket(MqttApplicationMessage applicationMessage) - { - if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage)); - if (_formatter == null) + public IMqttDataConverter DataConverter + { + get { - throw new InvalidOperationException("Protocol version not set or detected."); + ThrowIfFormatterNotSet(); + return _formatter.DataConverter; } - - return _formatter.ConvertApplicationMessageToPublishPacket(applicationMessage); } public ArraySegment Encode(MqttBasePacket packet) { - if (_formatter == null) - { - throw new InvalidOperationException("Protocol version not set or detected."); - } + ThrowIfFormatterNotSet(); return _formatter.Encode(packet); } public MqttBasePacket Decode(ReceivedMqttPacket receivedMqttPacket) { - if (_formatter == null) - { - throw new InvalidOperationException("Protocol version not set or detected."); - } + ThrowIfFormatterNotSet(); return _formatter.Decode(receivedMqttPacket); } @@ -64,7 +54,7 @@ namespace MQTTnet.Formatter { var protocolVersion = ParseProtocolVersion(receivedMqttPacket); - // Reset the position of the stream beacuse 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); @@ -138,5 +128,13 @@ namespace MQTTnet.Formatter throw new MqttProtocolViolationException($"Protocol '{protocolName}' not supported."); } + + private void ThrowIfFormatterNotSet() + { + if (_formatter == null) + { + throw new InvalidOperationException("Protocol version not set or detected."); + } + } } } diff --git a/Source/MQTTnet/Formatter/MqttPacketReader.cs b/Source/MQTTnet/Formatter/MqttPacketReader.cs index 97b1827..dfb633a 100644 --- a/Source/MQTTnet/Formatter/MqttPacketReader.cs +++ b/Source/MQTTnet/Formatter/MqttPacketReader.cs @@ -40,29 +40,30 @@ namespace MQTTnet.Formatter var hasRemainingLength = buffer[1] != 0; if (!hasRemainingLength) { - return new MqttFixedHeader(buffer[0], 0); + return new MqttFixedHeader(buffer[0], 0, totalBytesRead); } #if WINDOWS_UWP // UWP will have a dead lock when calling this not async. var bodyLength = await ReadBodyLengthAsync(buffer[1], cancellationToken).ConfigureAwait(false); #else - // Here the async/await pattern is not used becuase the overhead of context switches + // Here the async/await pattern is not used because the overhead of context switches // is too big for reading 1 byte in a row. We expect that the remaining data was sent // directly after the initial bytes. If the client disconnects just in this moment we // will get an exception anyway. var bodyLength = ReadBodyLength(buffer[1], cancellationToken); #endif - return new MqttFixedHeader(buffer[0], bodyLength); + totalBytesRead += bodyLength; + return new MqttFixedHeader(buffer[0], bodyLength, totalBytesRead); } #if !WINDOWS_UWP - private uint ReadBodyLength(byte initialEncodedByte, CancellationToken cancellationToken) + private int ReadBodyLength(byte initialEncodedByte, CancellationToken cancellationToken) { var offset = 0; var multiplier = 128; - var value = (uint)(initialEncodedByte & 127); + var value = (initialEncodedByte & 127); int encodedByte = initialEncodedByte; while ((encodedByte & 128) != 0) @@ -77,7 +78,7 @@ namespace MQTTnet.Formatter encodedByte = ReadByte(cancellationToken); - value += (uint)((encodedByte & 127) * multiplier); + value += (encodedByte & 127) * multiplier; multiplier *= 128; } @@ -100,11 +101,11 @@ namespace MQTTnet.Formatter #else - private async Task ReadBodyLengthAsync(byte initialEncodedByte, CancellationToken cancellationToken) + private async Task ReadBodyLengthAsync(byte initialEncodedByte, CancellationToken cancellationToken) { var offset = 0; var multiplier = 128; - var value = (uint)(initialEncodedByte & 127); + var value = initialEncodedByte & 127; int encodedByte = initialEncodedByte; while ((encodedByte & 128) != 0) @@ -119,7 +120,7 @@ namespace MQTTnet.Formatter encodedByte = await ReadByteAsync(cancellationToken).ConfigureAwait(false); - value += (uint)((encodedByte & 127) * multiplier); + value += (encodedByte & 127) * multiplier; multiplier *= 128; } diff --git a/Source/MQTTnet/Formatter/MqttPacketWriter.cs b/Source/MQTTnet/Formatter/MqttPacketWriter.cs index 0bebfbe..bf417f7 100644 --- a/Source/MQTTnet/Formatter/MqttPacketWriter.cs +++ b/Source/MQTTnet/Formatter/MqttPacketWriter.cs @@ -67,12 +67,14 @@ namespace MQTTnet.Formatter public void WriteWithLengthPrefix(byte[] value) { + if (value == null) throw new ArgumentNullException(nameof(value)); + EnsureAdditionalCapacity(value.Length + 2); Write((ushort)value.Length); Write(value, 0, value.Length); } - + public void Write(byte @byte) { EnsureAdditionalCapacity(1); @@ -173,7 +175,7 @@ namespace MQTTnet.Formatter { return; } - + while (newBufferLength < capacity) { newBufferLength *= 2; diff --git a/Source/MQTTnet/Formatter/V3/MqttV310DataConverter.cs b/Source/MQTTnet/Formatter/V3/MqttV310DataConverter.cs new file mode 100644 index 0000000..b643675 --- /dev/null +++ b/Source/MQTTnet/Formatter/V3/MqttV310DataConverter.cs @@ -0,0 +1,147 @@ +using System; +using System.Linq; +using MQTTnet.Client.Connecting; +using MQTTnet.Client.Options; +using MQTTnet.Client.Subscribing; +using MQTTnet.Client.Unsubscribing; +using MQTTnet.Exceptions; +using MQTTnet.Packets; +using MQTTnet.Protocol; + +namespace MQTTnet.Formatter.V3 +{ + public class MqttV310DataConverter : IMqttDataConverter + { + public MqttPublishPacket CreatePublishPacket(MqttApplicationMessage applicationMessage) + { + if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage)); + + if (applicationMessage.UserProperties?.Any() == true) + { + throw new MqttProtocolViolationException("User properties are not supported in MQTT version 3."); + } + + return new MqttPublishPacket + { + Topic = applicationMessage.Topic, + Payload = applicationMessage.Payload, + QualityOfServiceLevel = applicationMessage.QualityOfServiceLevel, + Retain = applicationMessage.Retain, + Dup = false + }; + } + + 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 + }; + } + + public MqttClientConnectResult CreateClientConnectResult(MqttConnAckPacket connAckPacket) + { + if (connAckPacket == null) throw new ArgumentNullException(nameof(connAckPacket)); + + MqttClientConnectResultCode resultCode; + switch (connAckPacket.ReturnCode.Value) + { + case MqttConnectReturnCode.ConnectionAccepted: + { + resultCode = MqttClientConnectResultCode.Success; + break; + } + + case MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion: + { + resultCode = MqttClientConnectResultCode.UnsupportedProtocolVersion; + break; + } + + case MqttConnectReturnCode.ConnectionRefusedNotAuthorized: + { + resultCode = MqttClientConnectResultCode.NotAuthorized; + break; + } + + case MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword: + { + resultCode = MqttClientConnectResultCode.BadUserNameOrPassword; + break; + } + + case MqttConnectReturnCode.ConnectionRefusedIdentifierRejected: + { + resultCode = MqttClientConnectResultCode.ClientIdentifierNotValid; + break; + } + + case MqttConnectReturnCode.ConnectionRefusedServerUnavailable: + { + resultCode = MqttClientConnectResultCode.ServerUnavailable; + break; + } + + default: + throw new MqttProtocolViolationException("Received unexpected return code."); + } + + return new MqttClientConnectResult + { + IsSessionPresent = connAckPacket.IsSessionPresent, + ResultCode = resultCode + }; + } + + public MqttConnectPacket CreateConnectPacket(MqttApplicationMessage willApplicationMessage, IMqttClientOptions options) + { + if (options == null) throw new ArgumentNullException(nameof(options)); + + return new MqttConnectPacket + { + ClientId = options.ClientId, + Username = options.Credentials?.Username, + Password = options.Credentials?.Password, + CleanSession = options.CleanSession, + KeepAlivePeriod = (ushort)options.KeepAlivePeriod.TotalSeconds, + WillMessage = willApplicationMessage + }; + } + + public MqttClientSubscribeResult CreateClientSubscribeResult(MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket) + { + if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket)); + if (subAckPacket == null) throw new ArgumentNullException(nameof(subAckPacket)); + + if (subAckPacket.ReturnCodes.Count != subscribePacket.TopicFilters.Count) + { + throw new MqttProtocolViolationException("The return codes are not matching the topic filters [MQTT-3.9.3-1]."); + } + + var result = new MqttClientSubscribeResult(); + + result.Items.AddRange(subscribePacket.TopicFilters.Select((t, i) => + new MqttClientSubscribeResultItem(t, (MqttClientSubscribeResultCode)subAckPacket.ReturnCodes[i]))); + + return result; + } + + public MqttClientUnsubscribeResult CreateClientUnsubscribeResult(MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket) + { + if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket)); + if (unsubAckPacket == null) throw new ArgumentNullException(nameof(unsubAckPacket)); + + var result = new MqttClientUnsubscribeResult(); + + result.Items.AddRange(unsubscribePacket.TopicFilters.Select((t, i) => + new MqttClientUnsubscribeResultItem(t, MqttClientUnsubscribeResultCode.Success))); + + return result; + } + } +} diff --git a/Source/MQTTnet/Formatter/V310/MqttV310PacketFormatter.cs b/Source/MQTTnet/Formatter/V3/MqttV310PacketFormatter.cs similarity index 72% rename from Source/MQTTnet/Formatter/V310/MqttV310PacketFormatter.cs rename to Source/MQTTnet/Formatter/V3/MqttV310PacketFormatter.cs index b1edc45..e9f8602 100644 --- a/Source/MQTTnet/Formatter/V310/MqttV310PacketFormatter.cs +++ b/Source/MQTTnet/Formatter/V3/MqttV310PacketFormatter.cs @@ -6,7 +6,7 @@ using MQTTnet.Internal; using MQTTnet.Packets; using MQTTnet.Protocol; -namespace MQTTnet.Formatter.V310 +namespace MQTTnet.Formatter.V3 { public class MqttV310PacketFormatter : IMqttPacketFormatter { @@ -14,6 +14,8 @@ namespace MQTTnet.Formatter.V310 private readonly MqttPacketWriter _packetWriter = new MqttPacketWriter(); + public IMqttDataConverter DataConverter { get; } = new MqttV310DataConverter(); + public ArraySegment Encode(MqttBasePacket packet) { if (packet == null) throw new ArgumentNullException(nameof(packet)); @@ -22,7 +24,7 @@ namespace MQTTnet.Formatter.V310 _packetWriter.Reset(); _packetWriter.Seek(5); - var fixedHeader = SerializePacket(packet, _packetWriter); + var fixedHeader = EncodePacket(packet, _packetWriter); var remainingLength = (uint)(_packetWriter.Length - 5); var remainingLengthBuffer = MqttPacketWriter.EncodeVariableByteInteger(remainingLength); @@ -30,7 +32,7 @@ namespace MQTTnet.Formatter.V310 var headerSize = FixedHeaderSize + remainingLengthBuffer.Count; var headerOffset = 5 - headerSize; - // Position cursor on correct offset on beginining of array (has leading 0x0) + // Position cursor on correct offset on beginning of array (has leading 0x0) _packetWriter.Seek(headerOffset); _packetWriter.Write(fixedHeader); _packetWriter.Write(remainingLengthBuffer.Array, remainingLengthBuffer.Offset, remainingLengthBuffer.Count); @@ -51,70 +53,54 @@ namespace MQTTnet.Formatter.V310 switch ((MqttControlPacketType)controlPacketType) { - case MqttControlPacketType.Connect: return DeserializeConnectPacket(receivedMqttPacket.Body); - case MqttControlPacketType.ConnAck: return DeserializeConnAckPacket(receivedMqttPacket.Body); + case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.Body); + case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.Body); case MqttControlPacketType.Disconnect: return new MqttDisconnectPacket(); - case MqttControlPacketType.Publish: return DeserializePublish(receivedMqttPacket); - case MqttControlPacketType.PubAck: return DeserializePubAck(receivedMqttPacket.Body); - case MqttControlPacketType.PubRec: return DeserializePubRec(receivedMqttPacket.Body); - case MqttControlPacketType.PubRel: return DeserializePubRel(receivedMqttPacket.Body); - case MqttControlPacketType.PubComp: return DeserializePubComp(receivedMqttPacket.Body); + case MqttControlPacketType.Publish: return DecodePublish(receivedMqttPacket); + case MqttControlPacketType.PubAck: return DecodePubAck(receivedMqttPacket.Body); + case MqttControlPacketType.PubRec: return DecodePubRec(receivedMqttPacket.Body); + case MqttControlPacketType.PubRel: return DecodePubRel(receivedMqttPacket.Body); + case MqttControlPacketType.PubComp: return DecodePubComp(receivedMqttPacket.Body); case MqttControlPacketType.PingReq: return new MqttPingReqPacket(); case MqttControlPacketType.PingResp: return new MqttPingRespPacket(); - case MqttControlPacketType.Subscribe: return DeserializeSubscribe(receivedMqttPacket.Body); - case MqttControlPacketType.SubAck: return DeserializeSubAckPacket(receivedMqttPacket.Body); - case MqttControlPacketType.Unsubscibe: return DeserializeUnsubscribe(receivedMqttPacket.Body); - case MqttControlPacketType.UnsubAck: return DeserializeUnsubAck(receivedMqttPacket.Body); + case MqttControlPacketType.Subscribe: return DecodeSubscribe(receivedMqttPacket.Body); + case MqttControlPacketType.SubAck: return DecodeSubAck(receivedMqttPacket.Body); + case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribe(receivedMqttPacket.Body); + case MqttControlPacketType.UnsubAck: return DecodeUnsubAck(receivedMqttPacket.Body); default: throw new MqttProtocolViolationException($"Packet type ({controlPacketType}) not supported."); } } - public virtual MqttPublishPacket ConvertApplicationMessageToPublishPacket(MqttApplicationMessage applicationMessage) - { - if (applicationMessage.UserProperties != null) - { - throw new MqttProtocolViolationException("User properties are not supported in MQTT version 3."); - } - - return new MqttPublishPacket - { - Topic = applicationMessage.Topic, - Payload = applicationMessage.Payload, - QualityOfServiceLevel = applicationMessage.QualityOfServiceLevel, - Retain = applicationMessage.Retain, - Dup = false - }; - } - public void FreeBuffer() { _packetWriter.FreeBuffer(); } - private byte SerializePacket(MqttBasePacket packet, MqttPacketWriter packetWriter) + private byte EncodePacket(MqttBasePacket packet, MqttPacketWriter packetWriter) { switch (packet) { - case MqttConnectPacket connectPacket: return SerializeConnectPacket(connectPacket, packetWriter); - case MqttConnAckPacket connAckPacket: return SerializeConnAckPacket(connAckPacket, packetWriter); - case MqttDisconnectPacket _: return SerializeEmptyPacket(MqttControlPacketType.Disconnect); - case MqttPingReqPacket _: return SerializeEmptyPacket(MqttControlPacketType.PingReq); - case MqttPingRespPacket _: return SerializeEmptyPacket(MqttControlPacketType.PingResp); - case MqttPublishPacket publishPacket: return SerializePublishPacket(publishPacket, packetWriter); - case MqttPubAckPacket pubAckPacket: return SerializePubAckPacket(pubAckPacket, packetWriter); - case MqttPubRecPacket pubRecPacket: return SerializePubRecPacket(pubRecPacket, packetWriter); - case MqttPubRelPacket pubRelPacket: return SerializePubRelPacket(pubRelPacket, packetWriter); - case MqttPubCompPacket pubCompPacket: return SerializePubCompPacket(pubCompPacket, packetWriter); - case MqttSubscribePacket subscribePacket: return SerializeSubscribePacket(subscribePacket, packetWriter); - case MqttSubAckPacket subAckPacket: return SerializeSubAckPacket(subAckPacket, packetWriter); - case MqttUnsubscribePacket unsubscribePacket: return SerializeUnsubscribePacket(unsubscribePacket, packetWriter); - case MqttUnsubAckPacket unsubAckPacket: return SerializeUnsubAckPacket(unsubAckPacket, packetWriter); + case MqttConnectPacket connectPacket: return EncodeConnectPacket(connectPacket, packetWriter); + case MqttConnAckPacket connAckPacket: return EncodeConnAckPacket(connAckPacket, packetWriter); + case MqttDisconnectPacket _: return EncodeEmptyPacket(MqttControlPacketType.Disconnect); + case MqttPingReqPacket _: return EncodeEmptyPacket(MqttControlPacketType.PingReq); + case MqttPingRespPacket _: return EncodeEmptyPacket(MqttControlPacketType.PingResp); + case MqttPublishPacket publishPacket: return EncodePublishPacket(publishPacket, packetWriter); + case MqttPubAckPacket pubAckPacket: return EncodePubAckPacket(pubAckPacket, packetWriter); + case MqttPubRecPacket pubRecPacket: return EncodePubRecPacket(pubRecPacket, packetWriter); + case MqttPubRelPacket pubRelPacket: return EncodePubRelPacket(pubRelPacket, packetWriter); + case MqttPubCompPacket pubCompPacket: return EncodePubCompPacket(pubCompPacket, packetWriter); + case MqttSubscribePacket subscribePacket: return EncodeSubscribePacket(subscribePacket, packetWriter); + case MqttSubAckPacket subAckPacket: return EncodeSubAckPacket(subAckPacket, packetWriter); + case MqttUnsubscribePacket unsubscribePacket: return EncodeUnsubscribePacket(unsubscribePacket, packetWriter); + case MqttUnsubAckPacket unsubAckPacket: return EncodeUnsubAckPacket(unsubAckPacket, packetWriter); + default: throw new MqttProtocolViolationException("Packet type invalid."); } } - private static MqttBasePacket DeserializeUnsubAck(MqttPacketBodyReader body) + private static MqttBasePacket DecodeUnsubAck(MqttPacketBodyReader body) { ThrowIfBodyIsEmpty(body); @@ -124,7 +110,7 @@ namespace MQTTnet.Formatter.V310 }; } - private static MqttBasePacket DeserializePubComp(MqttPacketBodyReader body) + private static MqttBasePacket DecodePubComp(MqttPacketBodyReader body) { ThrowIfBodyIsEmpty(body); @@ -134,7 +120,7 @@ namespace MQTTnet.Formatter.V310 }; } - private static MqttBasePacket DeserializePubRel(MqttPacketBodyReader body) + private static MqttBasePacket DecodePubRel(MqttPacketBodyReader body) { ThrowIfBodyIsEmpty(body); @@ -144,7 +130,7 @@ namespace MQTTnet.Formatter.V310 }; } - private static MqttBasePacket DeserializePubRec(MqttPacketBodyReader body) + private static MqttBasePacket DecodePubRec(MqttPacketBodyReader body) { ThrowIfBodyIsEmpty(body); @@ -154,7 +140,7 @@ namespace MQTTnet.Formatter.V310 }; } - private static MqttBasePacket DeserializePubAck(MqttPacketBodyReader body) + private static MqttBasePacket DecodePubAck(MqttPacketBodyReader body) { ThrowIfBodyIsEmpty(body); @@ -164,7 +150,7 @@ namespace MQTTnet.Formatter.V310 }; } - private static MqttBasePacket DeserializeUnsubscribe(MqttPacketBodyReader body) + private static MqttBasePacket DecodeUnsubscribe(MqttPacketBodyReader body) { ThrowIfBodyIsEmpty(body); @@ -181,7 +167,7 @@ namespace MQTTnet.Formatter.V310 return packet; } - private static MqttBasePacket DeserializeSubscribe(MqttPacketBodyReader body) + private static MqttBasePacket DecodeSubscribe(MqttPacketBodyReader body) { ThrowIfBodyIsEmpty(body); @@ -192,15 +178,19 @@ namespace MQTTnet.Formatter.V310 while (!body.EndOfStream) { - packet.TopicFilters.Add(new TopicFilter( - body.ReadStringWithLengthPrefix(), - (MqttQualityOfServiceLevel)body.ReadByte())); + var topicFilter = new TopicFilter + { + Topic = body.ReadStringWithLengthPrefix(), + QualityOfServiceLevel = (MqttQualityOfServiceLevel)body.ReadByte() + }; + + packet.TopicFilters.Add(topicFilter); } return packet; } - private static MqttBasePacket DeserializePublish(ReceivedMqttPacket receivedMqttPacket) + private static MqttBasePacket DecodePublish(ReceivedMqttPacket receivedMqttPacket) { ThrowIfBodyIsEmpty(receivedMqttPacket.Body); @@ -229,7 +219,7 @@ namespace MQTTnet.Formatter.V310 return packet; } - private MqttBasePacket DeserializeConnectPacket(MqttPacketBodyReader body) + private MqttBasePacket DecodeConnectPacket(MqttPacketBodyReader body) { ThrowIfBodyIsEmpty(body); @@ -238,7 +228,7 @@ namespace MQTTnet.Formatter.V310 ProtocolName = body.ReadStringWithLengthPrefix(), ProtocolLevel = body.ReadByte() }; - + var connectFlags = body.ReadByte(); if ((connectFlags & 0x1) > 0) { @@ -281,7 +271,7 @@ namespace MQTTnet.Formatter.V310 return packet; } - private static MqttBasePacket DeserializeSubAckPacket(MqttPacketBodyReader body) + private static MqttBasePacket DecodeSubAck(MqttPacketBodyReader body) { ThrowIfBodyIsEmpty(body); @@ -292,20 +282,20 @@ namespace MQTTnet.Formatter.V310 while (!body.EndOfStream) { - packet.SubscribeReturnCodes.Add((MqttSubscribeReturnCode)body.ReadByte()); + packet.ReturnCodes.Add((MqttSubscribeReturnCode)body.ReadByte()); } return packet; } - protected virtual MqttBasePacket DeserializeConnAckPacket(MqttPacketBodyReader body) + protected virtual MqttBasePacket DecodeConnAckPacket(MqttPacketBodyReader body) { ThrowIfBodyIsEmpty(body); var packet = new MqttConnAckPacket(); body.ReadByte(); // Reserved. - packet.ConnectReturnCode = (MqttConnectReturnCode)body.ReadByte(); + packet.ReturnCode = (MqttConnectReturnCode)body.ReadByte(); return packet; } @@ -329,7 +319,7 @@ namespace MQTTnet.Formatter.V310 } } - protected virtual byte SerializeConnectPacket(MqttConnectPacket packet, MqttPacketWriter packetWriter) + protected virtual byte EncodeConnectPacket(MqttConnectPacket packet, MqttPacketWriter packetWriter) { ValidateConnectPacket(packet); @@ -391,15 +381,15 @@ namespace MQTTnet.Formatter.V310 return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Connect); } - protected virtual byte SerializeConnAckPacket(MqttConnAckPacket packet, MqttPacketWriter packetWriter) + protected virtual byte EncodeConnAckPacket(MqttConnAckPacket packet, MqttPacketWriter packetWriter) { packetWriter.Write(0); // Reserved. - packetWriter.Write((byte)packet.ConnectReturnCode); + packetWriter.Write((byte)packet.ReturnCode.Value); return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck); } - private static byte SerializePubRelPacket(MqttPubRelPacket packet, MqttPacketWriter packetWriter) + private static byte EncodePubRelPacket(MqttPubRelPacket packet, MqttPacketWriter packetWriter) { if (!packet.PacketIdentifier.HasValue) { @@ -411,7 +401,7 @@ namespace MQTTnet.Formatter.V310 return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02); } - protected virtual byte SerializePublishPacket(MqttPublishPacket packet, MqttPacketWriter packetWriter) + private static byte EncodePublishPacket(MqttPublishPacket packet, MqttPacketWriter packetWriter) { ValidatePublishPacket(packet); @@ -456,7 +446,7 @@ namespace MQTTnet.Formatter.V310 return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Publish, fixedHeader); } - protected virtual byte SerializePubAckPacket(MqttPubAckPacket packet, MqttPacketWriter packetWriter) + private static byte EncodePubAckPacket(MqttPubAckPacket packet, MqttPacketWriter packetWriter) { if (!packet.PacketIdentifier.HasValue) { @@ -468,7 +458,7 @@ namespace MQTTnet.Formatter.V310 return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck); } - private static byte SerializePubRecPacket(MqttPubRecPacket packet, MqttPacketWriter packetWriter) + private static byte EncodePubRecPacket(MqttPubRecPacket packet, MqttPacketWriter packetWriter) { if (!packet.PacketIdentifier.HasValue) { @@ -480,7 +470,7 @@ namespace MQTTnet.Formatter.V310 return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec); } - private static byte SerializePubCompPacket(MqttPubCompPacket packet, MqttPacketWriter packetWriter) + private static byte EncodePubCompPacket(MqttPubCompPacket packet, MqttPacketWriter packetWriter) { if (!packet.PacketIdentifier.HasValue) { @@ -492,7 +482,7 @@ namespace MQTTnet.Formatter.V310 return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp); } - private static byte SerializeSubscribePacket(MqttSubscribePacket packet, MqttPacketWriter packetWriter) + private static byte EncodeSubscribePacket(MqttSubscribePacket packet, MqttPacketWriter packetWriter) { if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3]."); @@ -515,7 +505,7 @@ namespace MQTTnet.Formatter.V310 return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Subscribe, 0x02); } - private static byte SerializeSubAckPacket(MqttSubAckPacket packet, MqttPacketWriter packetWriter) + private static byte EncodeSubAckPacket(MqttSubAckPacket packet, MqttPacketWriter packetWriter) { if (!packet.PacketIdentifier.HasValue) { @@ -524,9 +514,9 @@ namespace MQTTnet.Formatter.V310 packetWriter.Write(packet.PacketIdentifier.Value); - if (packet.SubscribeReturnCodes?.Any() == true) + if (packet.ReturnCodes?.Any() == true) { - foreach (var packetSubscribeReturnCode in packet.SubscribeReturnCodes) + foreach (var packetSubscribeReturnCode in packet.ReturnCodes) { packetWriter.Write((byte)packetSubscribeReturnCode); } @@ -535,7 +525,7 @@ namespace MQTTnet.Formatter.V310 return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.SubAck); } - private static byte SerializeUnsubscribePacket(MqttUnsubscribePacket packet, MqttPacketWriter packetWriter) + private static byte EncodeUnsubscribePacket(MqttUnsubscribePacket packet, MqttPacketWriter packetWriter) { if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2]."); @@ -557,7 +547,7 @@ namespace MQTTnet.Formatter.V310 return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02); } - private static byte SerializeUnsubAckPacket(MqttUnsubAckPacket packet, MqttPacketWriter packetWriter) + private static byte EncodeUnsubAckPacket(MqttUnsubAckPacket packet, MqttPacketWriter packetWriter) { if (!packet.PacketIdentifier.HasValue) { @@ -568,7 +558,7 @@ namespace MQTTnet.Formatter.V310 return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck); } - private static byte SerializeEmptyPacket(MqttControlPacketType type) + private static byte EncodeEmptyPacket(MqttControlPacketType type) { return MqttPacketWriter.BuildFixedHeader(type); } diff --git a/Source/MQTTnet/Formatter/V311/MqttV311PacketFormatter.cs b/Source/MQTTnet/Formatter/V3/MqttV311PacketFormatter.cs similarity index 83% rename from Source/MQTTnet/Formatter/V311/MqttV311PacketFormatter.cs rename to Source/MQTTnet/Formatter/V3/MqttV311PacketFormatter.cs index a02b9b3..0bc60a9 100644 --- a/Source/MQTTnet/Formatter/V311/MqttV311PacketFormatter.cs +++ b/Source/MQTTnet/Formatter/V3/MqttV311PacketFormatter.cs @@ -1,13 +1,12 @@ using MQTTnet.Exceptions; -using MQTTnet.Formatter.V310; using MQTTnet.Packets; using MQTTnet.Protocol; -namespace MQTTnet.Formatter.V311 +namespace MQTTnet.Formatter.V3 { public class MqttV311PacketFormatter : MqttV310PacketFormatter { - protected override byte SerializeConnectPacket(MqttConnectPacket packet, MqttPacketWriter packetWriter) + protected override byte EncodeConnectPacket(MqttConnectPacket packet, MqttPacketWriter packetWriter) { ValidateConnectPacket(packet); @@ -69,7 +68,7 @@ namespace MQTTnet.Formatter.V311 return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Connect); } - protected override byte SerializeConnAckPacket(MqttConnAckPacket packet, MqttPacketWriter packetWriter) + protected override byte EncodeConnAckPacket(MqttConnAckPacket packet, MqttPacketWriter packetWriter) { byte connectAcknowledgeFlags = 0x0; if (packet.IsSessionPresent) @@ -78,12 +77,12 @@ namespace MQTTnet.Formatter.V311 } packetWriter.Write(connectAcknowledgeFlags); - packetWriter.Write((byte)packet.ConnectReturnCode); + packetWriter.Write((byte)packet.ReturnCode); return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck); } - protected override MqttBasePacket DeserializeConnAckPacket(MqttPacketBodyReader body) + protected override MqttBasePacket DecodeConnAckPacket(MqttPacketBodyReader body) { ThrowIfBodyIsEmpty(body); @@ -92,7 +91,7 @@ namespace MQTTnet.Formatter.V311 var acknowledgeFlags = body.ReadByte(); packet.IsSessionPresent = (acknowledgeFlags & 0x1) > 0; - packet.ConnectReturnCode = (MqttConnectReturnCode)body.ReadByte(); + packet.ReturnCode = (MqttConnectReturnCode)body.ReadByte(); return packet; } diff --git a/Source/MQTTnet/Formatter/V5/MqttV500DataConverter.cs b/Source/MQTTnet/Formatter/V5/MqttV500DataConverter.cs new file mode 100644 index 0000000..5a26a67 --- /dev/null +++ b/Source/MQTTnet/Formatter/V5/MqttV500DataConverter.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using MQTTnet.Client.Connecting; +using MQTTnet.Client.Options; +using MQTTnet.Client.Subscribing; +using MQTTnet.Client.Unsubscribing; +using MQTTnet.Exceptions; +using MQTTnet.Packets; + +namespace MQTTnet.Formatter.V5 +{ + public class MqttV500DataConverter : IMqttDataConverter + { + public MqttPublishPacket CreatePublishPacket(MqttApplicationMessage applicationMessage) + { + if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage)); + + var packet = new MqttPublishPacket + { + Topic = applicationMessage.Topic, + Payload = applicationMessage.Payload, + QualityOfServiceLevel = applicationMessage.QualityOfServiceLevel, + Retain = applicationMessage.Retain, + Dup = false, + Properties = new MqttPublishPacketProperties + { + ContentType = applicationMessage.ContentType, + CorrelationData = applicationMessage.CorrelationData, + MessageExpiryInterval = applicationMessage.MessageExpiryInterval, + PayloadFormatIndicator = applicationMessage.PayloadFormatIndicator, + ResponseTopic = applicationMessage.ResponseTopic, + SubscriptionIdentifier = applicationMessage.SubscriptionIdentifier, + TopicAlias = applicationMessage.TopicAlias + } + }; + + if (applicationMessage.UserProperties != null) + { + packet.Properties.UserProperties.AddRange(applicationMessage.UserProperties); + } + + return packet; + } + + public MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket) + { + return new MqttApplicationMessage + { + Topic = publishPacket.Topic, + Payload = publishPacket.Payload, + QualityOfServiceLevel = publishPacket.QualityOfServiceLevel, + Retain = publishPacket.Retain, + UserProperties = publishPacket.Properties?.UserProperties ?? new List() + }; + } + + public MqttClientConnectResult CreateClientConnectResult(MqttConnAckPacket connAckPacket) + { + if (connAckPacket == null) throw new ArgumentNullException(nameof(connAckPacket)); + + return new MqttClientConnectResult + { + IsSessionPresent = connAckPacket.IsSessionPresent, + ResultCode = (MqttClientConnectResultCode)connAckPacket.ReasonCode.Value + }; + } + + public MqttConnectPacket CreateConnectPacket(MqttApplicationMessage willApplicationMessage, IMqttClientOptions options) + { + if (options == null) throw new ArgumentNullException(nameof(options)); + + return new MqttConnectPacket + { + ClientId = options.ClientId, + Username = options.Credentials?.Username, + Password = options.Credentials?.Password, + CleanSession = options.CleanSession, + KeepAlivePeriod = (ushort)options.KeepAlivePeriod.TotalSeconds, + WillMessage = willApplicationMessage, + Properties = new MqttConnectPacketProperties + { + AuthenticationMethod = options.AuthenticationMethod, + AuthenticationData = options.AuthenticationData, + WillDelayInterval = options.WillDelayInterval, + MaximumPacketSize = options.MaximumPacketSize, + ReceiveMaximum = options.ReceiveMaximum, + RequestProblemInformation = options.RequestProblemInformation, + RequestResponseInformation = options.RequestResponseInformation, + SessionExpiryInterval = options.SessionExpiryInterval, + TopicAliasMaximum = options.TopicAliasMaximum + } + }; + } + + public MqttClientSubscribeResult CreateClientSubscribeResult(MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket) + { + if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket)); + if (subAckPacket == null) throw new ArgumentNullException(nameof(subAckPacket)); + + if (subAckPacket.ReasonCodes.Count != subscribePacket.TopicFilters.Count) + { + throw new MqttProtocolViolationException("The reason codes are not matching the topic filters [MQTT-3.9.3-1]."); + } + + var result = new MqttClientSubscribeResult(); + + result.Items.AddRange(subscribePacket.TopicFilters.Select((t, i) => + new MqttClientSubscribeResultItem(t, (MqttClientSubscribeResultCode)subAckPacket.ReasonCodes[i]))); + + return result; + } + + public MqttClientUnsubscribeResult CreateClientUnsubscribeResult(MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket) + { + if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket)); + if (unsubAckPacket == null) throw new ArgumentNullException(nameof(unsubAckPacket)); + + if (unsubAckPacket.ReasonCodes.Count != unsubscribePacket.TopicFilters.Count) + { + throw new MqttProtocolViolationException("The return codes are not matching the topic filters [MQTT-3.9.3-1]."); + } + + var result = new MqttClientUnsubscribeResult(); + + result.Items.AddRange(unsubscribePacket.TopicFilters.Select((t, i) => + new MqttClientUnsubscribeResultItem(t, (MqttClientUnsubscribeResultCode)unsubAckPacket.ReasonCodes[i]))); + + return result; + } + } +} diff --git a/Source/MQTTnet/Formatter/V5/MqttV500PacketDecoder.cs b/Source/MQTTnet/Formatter/V5/MqttV500PacketDecoder.cs new file mode 100644 index 0000000..a133dc6 --- /dev/null +++ b/Source/MQTTnet/Formatter/V5/MqttV500PacketDecoder.cs @@ -0,0 +1,717 @@ +using System; +using System.Linq; +using MQTTnet.Adapter; +using MQTTnet.Exceptions; +using MQTTnet.Packets; +using MQTTnet.Protocol; + +namespace MQTTnet.Formatter.V5 +{ + public class MqttV500PacketDecoder + { + public MqttBasePacket Decode(ReceivedMqttPacket receivedMqttPacket) + { + if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket)); + + var controlPacketType = receivedMqttPacket.FixedHeader >> 4; + if (controlPacketType < 1 || controlPacketType > 15) + { + throw new MqttProtocolViolationException($"The packet type is invalid ({controlPacketType})."); + } + + 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.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); + + default: throw new MqttProtocolViolationException($"Packet type ({controlPacketType}) not supported."); + } + } + + private static MqttBasePacket DecodeConnectPacket(MqttPacketBodyReader body) + { + ThrowIfBodyIsEmpty(body); + + var packet = new MqttConnectPacket + { + Properties = new MqttConnectPacketProperties() + }; + + var protocolName = body.ReadStringWithLengthPrefix(); + var protocolVersion = body.ReadByte(); + + if (protocolName != "MQTT" && protocolVersion != 5) + { + throw new MqttProtocolViolationException("MQTT protocol name and version do not match MQTT v5."); + } + + var connectFlags = body.ReadByte(); + + var cleanSessionFlag = (connectFlags & 0x02) > 0; + var willMessageFlag = (connectFlags & 0x04) > 0; + var willMessageQoS = (byte)(connectFlags >> 3 & 3); + var willMessageRetainFlag = (connectFlags & 0x20) > 0; + var passwordFlag = (connectFlags & 0x40) > 0; + var usernameFlag = (connectFlags & 0x80) > 0; + + packet.CleanSession = cleanSessionFlag; + + if (willMessageFlag) + { + packet.WillMessage = new MqttApplicationMessage + { + QualityOfServiceLevel = (MqttQualityOfServiceLevel)willMessageQoS, + Retain = willMessageRetainFlag + }; + } + + packet.KeepAlivePeriod = body.ReadTwoByteInteger(); + + var propertiesReader = new MqttV500PropertiesReader(body); + while (propertiesReader.MoveNext()) + { + if (propertiesReader.CurrentPropertyId == MqttPropertyId.SessionExpiryInterval) + { + packet.Properties.SessionExpiryInterval = propertiesReader.ReadSessionExpiryInterval(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AuthenticationMethod) + { + packet.Properties.AuthenticationMethod = propertiesReader.ReadAuthenticationMethod(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.WillDelayInterval) + { + packet.Properties.WillDelayInterval = propertiesReader.ReadWillDelayInterval(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AuthenticationData) + { + packet.Properties.AuthenticationData = propertiesReader.ReadAuthenticationData(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReceiveMaximum) + { + packet.Properties.ReceiveMaximum = propertiesReader.ReadReceiveMaximum(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.TopicAliasMaximum) + { + packet.Properties.TopicAliasMaximum = propertiesReader.ReadTopicAliasMaximum(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.MaximumPacketSize) + { + packet.Properties.MaximumPacketSize = propertiesReader.ReadMaximumPacketSize(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.RequestResponseInformation) + { + packet.Properties.RequestResponseInformation = propertiesReader.RequestResponseInformation(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.RequestProblemInformation) + { + packet.Properties.RequestProblemInformation = propertiesReader.RequestProblemInformation(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty) + { + propertiesReader.FillUserProperties(packet.Properties.UserProperties); + } + else + { + propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttConnAckPacket)); + } + } + + packet.ClientId = body.ReadStringWithLengthPrefix(); + + if (packet.WillMessage != null) + { + packet.WillMessage.Topic = body.ReadStringWithLengthPrefix(); + packet.WillMessage.Payload = body.ReadWithLengthPrefix().ToArray(); + } + + if (usernameFlag) + { + packet.Username = body.ReadStringWithLengthPrefix(); + } + + if (passwordFlag) + { + packet.Password = body.ReadStringWithLengthPrefix(); + } + + return packet; + } + + private static MqttBasePacket DecodeConnAckPacket(MqttPacketBodyReader body) + { + ThrowIfBodyIsEmpty(body); + + var acknowledgeFlags = body.ReadByte(); + + var packet = new MqttConnAckPacket + { + IsSessionPresent = (acknowledgeFlags & 0x1) > 0, + ReasonCode = (MqttConnectReasonCode)body.ReadByte(), + Properties = new MqttConnAckPacketProperties() + }; + + var propertiesReader = new MqttV500PropertiesReader(body); + while (propertiesReader.MoveNext()) + { + if (propertiesReader.CurrentPropertyId == MqttPropertyId.SessionExpiryInterval) + { + packet.Properties.SessionExpiryInterval = propertiesReader.ReadSessionExpiryInterval(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AuthenticationMethod) + { + packet.Properties.AuthenticationMethod = propertiesReader.ReadAuthenticationMethod(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AuthenticationData) + { + packet.Properties.AuthenticationData = propertiesReader.ReadAuthenticationData(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.RetainAvailable) + { + packet.Properties.RetainAvailable = propertiesReader.ReadRetainAvailable(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReceiveMaximum) + { + packet.Properties.ReceiveMaximum = propertiesReader.ReadReceiveMaximum(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AssignedClientIdentifer) + { + packet.Properties.AssignedClientIdentifier = propertiesReader.ReadAssignedClientIdentifier(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.TopicAliasMaximum) + { + packet.Properties.TopicAliasMaximum = propertiesReader.ReadTopicAliasMaximum(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString) + { + packet.Properties.ReasonString = propertiesReader.ReadReasonString(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.MaximumPacketSize) + { + packet.Properties.MaximumPacketSize = propertiesReader.ReadMaximumPacketSize(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.WildcardSubscriptionAvailable) + { + packet.Properties.WildcardSubscriptionAvailable = propertiesReader.ReadWildcardSubscriptionAvailable(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.SubscriptionIdentifiersAvailable) + { + packet.Properties.SubscriptionIdentifiersAvailable = propertiesReader.ReadSubscriptionIdentifiersAvailable(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.SharedSubscriptionAvailable) + { + packet.Properties.SharedSubscriptionAvailable = propertiesReader.ReadSharedSubscriptionAvailable(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ServerKeepAlive) + { + packet.Properties.ServerKeepAlive = propertiesReader.ReadServerKeepAlive(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ResponseInformation) + { + packet.Properties.ResponseInformation = propertiesReader.ReadResponseInformation(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ServerReference) + { + packet.Properties.ServerReference = propertiesReader.ReadServerReference(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty) + { + propertiesReader.FillUserProperties(packet.Properties.UserProperties); + } + else + { + propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttConnAckPacket)); + } + } + + return packet; + } + + private static MqttBasePacket DecodeDisconnectPacket(MqttPacketBodyReader body) + { + ThrowIfBodyIsEmpty(body); + + var packet = new MqttDisconnectPacket + { + ReasonCode = (MqttDisconnectReasonCode)body.ReadByte(), + Properties = new MqttDisconnectPacketProperties() + }; + + var propertiesReader = new MqttV500PropertiesReader(body); + while (propertiesReader.MoveNext()) + { + if (propertiesReader.CurrentPropertyId == MqttPropertyId.SessionExpiryInterval) + { + packet.Properties.SessionExpiryInterval = propertiesReader.ReadSessionExpiryInterval(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString) + { + packet.Properties.ReasonString = propertiesReader.ReadReasonString(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ServerReference) + { + packet.Properties.ServerReference = propertiesReader.ReadServerReference(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty) + { + propertiesReader.FillUserProperties(packet.Properties.UserProperties); + } + else + { + propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttDisconnectPacket)); + } + } + + return packet; + } + + private static MqttBasePacket DecodeSubscribePacket(MqttPacketBodyReader body) + { + ThrowIfBodyIsEmpty(body); + + var packet = new MqttSubscribePacket + { + PacketIdentifier = body.ReadTwoByteInteger(), + Properties = new MqttSubscribePacketProperties() + }; + + var propertiesReader = new MqttV500PropertiesReader(body); + while (propertiesReader.MoveNext()) + { + if (propertiesReader.CurrentPropertyId == MqttPropertyId.SubscriptionIdentifier) + { + packet.Properties.SubscriptionIdentifier = propertiesReader.ReadSubscriptionIdentifier(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty) + { + propertiesReader.FillUserProperties(packet.Properties.UserProperties); + } + else + { + propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttSubscribePacket)); + } + } + + while (!body.EndOfStream) + { + var topic = body.ReadStringWithLengthPrefix(); + var options = body.ReadByte(); + + var qos = (MqttQualityOfServiceLevel)(options & 3); + var noLocal = (options & (1 << 2)) > 0; + var retainAsPublished = (options & (1 << 3)) > 0; + var retainHandling = (MqttRetainHandling)((options >> 4) & 3); + + packet.TopicFilters.Add(new TopicFilter + { + Topic = topic, + QualityOfServiceLevel = qos, + NoLocal = noLocal, + RetainAsPublished = retainAsPublished, + RetainHandling = retainHandling + }); + } + + return packet; + } + + private static MqttBasePacket DecodeSubAckPacket(MqttPacketBodyReader body) + { + ThrowIfBodyIsEmpty(body); + + var packet = new MqttSubAckPacket + { + PacketIdentifier = body.ReadTwoByteInteger(), + Properties = new MqttSubAckPacketProperties() + }; + + var propertiesReader = new MqttV500PropertiesReader(body); + while (propertiesReader.MoveNext()) + { + if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString) + { + packet.Properties.ReasonString = propertiesReader.ReadReasonString(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty) + { + propertiesReader.FillUserProperties(packet.Properties.UserProperties); + } + else + { + propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttSubAckPacket)); + } + } + + while (!body.EndOfStream) + { + var reasonCode = (MqttSubscribeReasonCode)body.ReadByte(); + packet.ReasonCodes.Add(reasonCode); + } + + return packet; + } + + private static MqttBasePacket DecodeUnsubscribePacket(MqttPacketBodyReader body) + { + ThrowIfBodyIsEmpty(body); + + var packet = new MqttUnsubscribePacket + { + PacketIdentifier = body.ReadTwoByteInteger(), + Properties = new MqttUnsubscribePacketProperties() + }; + + var propertiesReader = new MqttV500PropertiesReader(body); + while (propertiesReader.MoveNext()) + { + if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty) + { + propertiesReader.FillUserProperties(packet.Properties.UserProperties); + } + else + { + propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttUnsubscribePacket)); + } + } + + while (!body.EndOfStream) + { + packet.TopicFilters.Add(body.ReadStringWithLengthPrefix()); + } + + return packet; + } + + private static MqttBasePacket DecodeUnsubAckPacket(MqttPacketBodyReader body) + { + ThrowIfBodyIsEmpty(body); + + var packet = new MqttUnsubAckPacket + { + PacketIdentifier = body.ReadTwoByteInteger(), + Properties = new MqttUnsubAckPacketProperties() + }; + + var propertiesReader = new MqttV500PropertiesReader(body); + while (propertiesReader.MoveNext()) + { + if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString) + { + packet.Properties.ReasonString = propertiesReader.ReadReasonString(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty) + { + propertiesReader.FillUserProperties(packet.Properties.UserProperties); + } + else + { + propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttUnsubAckPacket)); + } + } + + while (!body.EndOfStream) + { + var reasonCode = (MqttUnsubscribeReasonCode)body.ReadByte(); + packet.ReasonCodes.Add(reasonCode); + } + + return packet; + } + + private static MqttBasePacket DecodePingReqPacket() + { + return new MqttPingReqPacket(); + } + + private static MqttBasePacket DecodePingRespPacket() + { + return new MqttPingRespPacket(); + } + + private static MqttBasePacket DecodePublishPacket(byte header, MqttPacketBodyReader body) + { + ThrowIfBodyIsEmpty(body); + + var retain = (header & 1) > 0; + var qos = (MqttQualityOfServiceLevel)(header >> 1 & 3); + var dup = (header >> 3 & 1) > 0; + + var packet = new MqttPublishPacket + { + Topic = body.ReadStringWithLengthPrefix(), + Retain = retain, + QualityOfServiceLevel = qos, + Dup = dup, + Properties = new MqttPublishPacketProperties() + }; + + if (qos > 0) + { + packet.PacketIdentifier = body.ReadTwoByteInteger(); + } + + var propertiesReader = new MqttV500PropertiesReader(body); + while (propertiesReader.MoveNext()) + { + if (propertiesReader.CurrentPropertyId == MqttPropertyId.PayloadFormatIndicator) + { + packet.Properties.PayloadFormatIndicator = propertiesReader.ReadPayloadFormatIndicator(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.MessageExpiryInterval) + { + packet.Properties.MessageExpiryInterval = propertiesReader.ReadMessageExpiryInterval(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.TopicAlias) + { + packet.Properties.TopicAlias = propertiesReader.ReadTopicAlias(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ResponseTopic) + { + packet.Properties.ResponseTopic = propertiesReader.ReadResponseTopic(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.CorrelationData) + { + packet.Properties.CorrelationData = propertiesReader.ReadCorrelationData(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.SubscriptionIdentifier) + { + packet.Properties.SubscriptionIdentifier = propertiesReader.ReadSubscriptionIdentifier(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ContentType) + { + packet.Properties.ContentType = propertiesReader.ReadContentType(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty) + { + propertiesReader.FillUserProperties(packet.Properties.UserProperties); + } + else + { + propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttPublishPacket)); + } + } + + packet.Payload = body.ReadRemainingData().ToArray(); + + return packet; + } + + private static MqttBasePacket DecodePubAckPacket(MqttPacketBodyReader body) + { + ThrowIfBodyIsEmpty(body); + + var packet = new MqttPubAckPacket + { + PacketIdentifier = body.ReadTwoByteInteger(), + Properties = new MqttPubAckPacketProperties() + }; + + if (body.EndOfStream) + { + packet.ReasonCode = MqttPubAckReasonCode.Success; + return packet; + } + + packet.ReasonCode = (MqttPubAckReasonCode)body.ReadByte(); + + var propertiesReader = new MqttV500PropertiesReader(body); + while (propertiesReader.MoveNext()) + { + if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString) + { + packet.Properties.ReasonString = propertiesReader.ReadReasonString(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty) + { + propertiesReader.FillUserProperties(packet.Properties.UserProperties); + } + else + { + propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttPubAckPacket)); + } + } + + return packet; + } + + private static MqttBasePacket DecodePubRecPacket(MqttPacketBodyReader body) + { + ThrowIfBodyIsEmpty(body); + + var packet = new MqttPubRecPacket + { + PacketIdentifier = body.ReadTwoByteInteger(), + Properties = new MqttPubRecPacketProperties() + }; + + if (body.EndOfStream) + { + packet.ReasonCode = MqttPubRecReasonCode.Success; + return packet; + } + + packet.ReasonCode = (MqttPubRecReasonCode)body.ReadByte(); + + var propertiesReader = new MqttV500PropertiesReader(body); + while (propertiesReader.MoveNext()) + { + if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString) + { + packet.Properties.ReasonString = propertiesReader.ReadReasonString(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty) + { + propertiesReader.FillUserProperties(packet.Properties.UserProperties); + } + else + { + propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttPubRecPacket)); + } + } + + return packet; + } + + private static MqttBasePacket DecodePubRelPacket(MqttPacketBodyReader body) + { + ThrowIfBodyIsEmpty(body); + + var packet = new MqttPubRelPacket + { + PacketIdentifier = body.ReadTwoByteInteger(), + Properties = new MqttPubRelPacketProperties() + }; + + if (body.EndOfStream) + { + packet.ReasonCode = MqttPubRelReasonCode.Success; + return packet; + } + + packet.ReasonCode = (MqttPubRelReasonCode)body.ReadByte(); + + var propertiesReader = new MqttV500PropertiesReader(body); + while (propertiesReader.MoveNext()) + { + if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString) + { + packet.Properties.ReasonString = propertiesReader.ReadReasonString(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty) + { + propertiesReader.FillUserProperties(packet.Properties.UserProperties); + } + else + { + propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttPubRelPacket)); + } + } + + return packet; + } + + private static MqttBasePacket DecodePubCompPacket(MqttPacketBodyReader body) + { + ThrowIfBodyIsEmpty(body); + + var packet = new MqttPubCompPacket + { + PacketIdentifier = body.ReadTwoByteInteger(), + Properties = new MqttPubCompPacketProperties() + }; + + if (body.EndOfStream) + { + packet.ReasonCode = MqttPubCompReasonCode.Success; + return packet; + } + + packet.ReasonCode = (MqttPubCompReasonCode)body.ReadByte(); + + var propertiesReader = new MqttV500PropertiesReader(body); + while (propertiesReader.MoveNext()) + { + if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString) + { + packet.Properties.ReasonString = propertiesReader.ReadReasonString(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty) + { + propertiesReader.FillUserProperties(packet.Properties.UserProperties); + } + else + { + propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttPubCompPacket)); + } + } + + return packet; + } + + private static MqttBasePacket DecodeAuthPacket(MqttPacketBodyReader body) + { + ThrowIfBodyIsEmpty(body); + + var packet = new MqttAuthPacket + { + Properties = new MqttAuthPacketProperties() + }; + + if (body.EndOfStream) + { + packet.ReasonCode = MqttAuthenticateReasonCode.Success; + return packet; + } + + packet.ReasonCode = (MqttAuthenticateReasonCode)body.ReadByte(); + + var propertiesReader = new MqttV500PropertiesReader(body); + while (propertiesReader.MoveNext()) + { + if (propertiesReader.CurrentPropertyId == MqttPropertyId.AuthenticationMethod) + { + packet.Properties.AuthenticationMethod = propertiesReader.ReadAuthenticationMethod(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AuthenticationData) + { + packet.Properties.AuthenticationData = propertiesReader.ReadAuthenticationData(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString) + { + packet.Properties.ReasonString = propertiesReader.ReadReasonString(); + } + else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty) + { + propertiesReader.FillUserProperties(packet.Properties.UserProperties); + } + else + { + propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttAuthPacket)); + } + } + + return packet; + } + + // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local + private static void ThrowIfBodyIsEmpty(MqttPacketBodyReader body) + { + if (body == null || body.Length == 0) + { + throw new MqttProtocolViolationException("Data from the body is required but not present."); + } + } + } +} diff --git a/Source/MQTTnet/Formatter/V5/MqttV500PacketEncoder.cs b/Source/MQTTnet/Formatter/V5/MqttV500PacketEncoder.cs new file mode 100644 index 0000000..0c0019c --- /dev/null +++ b/Source/MQTTnet/Formatter/V5/MqttV500PacketEncoder.cs @@ -0,0 +1,572 @@ +using System; +using System.Linq; +using MQTTnet.Exceptions; +using MQTTnet.Packets; +using MQTTnet.Protocol; + +namespace MQTTnet.Formatter.V5 +{ + public class MqttV500PacketEncoder + { + private readonly MqttPacketWriter _packetWriter = new MqttPacketWriter(); + + public ArraySegment Encode(MqttBasePacket packet) + { + if (packet == null) throw new ArgumentNullException(nameof(packet)); + + // Leave enough head space for max header size (fixed + 4 variable remaining length = 5 bytes) + _packetWriter.Reset(); + _packetWriter.Seek(5); + + var fixedHeader = EncodePacket(packet, _packetWriter); + var remainingLength = (uint)(_packetWriter.Length - 5); + + var remainingLengthBuffer = MqttPacketWriter.EncodeVariableByteInteger(remainingLength); + + var headerSize = 1 + remainingLengthBuffer.Count; + var headerOffset = 5 - headerSize; + + // Position cursor on correct offset on beginning of array (has leading 0x0) + _packetWriter.Seek(headerOffset); + _packetWriter.Write(fixedHeader); + _packetWriter.Write(remainingLengthBuffer.Array, remainingLengthBuffer.Offset, remainingLengthBuffer.Count); + + var buffer = _packetWriter.GetBuffer(); + return new ArraySegment(buffer, headerOffset, _packetWriter.Length - headerOffset); + } + + public void FreeBuffer() + { + _packetWriter.FreeBuffer(); + } + + private static byte EncodePacket(MqttBasePacket packet, MqttPacketWriter packetWriter) + { + switch (packet) + { + case MqttConnectPacket connectPacket: return EncodeConnectPacket(connectPacket, packetWriter); + case MqttConnAckPacket connAckPacket: return EncodeConnAckPacket(connAckPacket, packetWriter); + case MqttDisconnectPacket disconnectPacket: return EncodeDisconnectPacket(disconnectPacket, packetWriter); + case MqttPingReqPacket _: return EncodePingReqPacket(); + case MqttPingRespPacket _: return EncodePingRespPacket(); + case MqttPublishPacket publishPacket: return EncodePublishPacket(publishPacket, packetWriter); + case MqttPubAckPacket pubAckPacket: return EncodePubAckPacket(pubAckPacket, packetWriter); + case MqttPubRecPacket pubRecPacket: return EncodePubRecPacket(pubRecPacket, packetWriter); + case MqttPubRelPacket pubRelPacket: return EncodePubRelPacket(pubRelPacket, packetWriter); + case MqttPubCompPacket pubCompPacket: return EncodePubCompPacket(pubCompPacket, packetWriter); + case MqttSubscribePacket subscribePacket: return EncodeSubscribePacket(subscribePacket, packetWriter); + case MqttSubAckPacket subAckPacket: return EncodeSubAckPacket(subAckPacket, packetWriter); + case MqttUnsubscribePacket unsubscribePacket: return EncodeUnsubscribePacket(unsubscribePacket, packetWriter); + case MqttUnsubAckPacket unsubAckPacket: return EncodeUnsubAckPacket(unsubAckPacket, packetWriter); + case MqttAuthPacket authPacket: return EncodeAuthPacket(authPacket, packetWriter); + + default: throw new MqttProtocolViolationException("Packet type invalid."); + } + } + + private static byte EncodeConnectPacket(MqttConnectPacket packet, MqttPacketWriter packetWriter) + { + if (packet == null) throw new ArgumentNullException(nameof(packet)); + if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter)); + + if (string.IsNullOrEmpty(packet.ClientId) && !packet.CleanSession) + { + throw new MqttProtocolViolationException("CleanSession must be set if ClientId is empty [MQTT-3.1.3-7]."); + } + + packetWriter.WriteWithLengthPrefix("MQTT"); + packetWriter.Write(5); // [3.1.2.2 Protocol Version] + + byte connectFlags = 0x0; + if (packet.CleanSession) + { + connectFlags |= 0x2; + } + + if (packet.WillMessage != null) + { + connectFlags |= 0x4; + connectFlags |= (byte)((byte)packet.WillMessage.QualityOfServiceLevel << 3); + + if (packet.WillMessage.Retain) + { + connectFlags |= 0x20; + } + } + + if (packet.Password != null && packet.Username == null) + { + throw new MqttProtocolViolationException("If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22]."); + } + + if (packet.Password != null) + { + connectFlags |= 0x40; + } + + if (packet.Username != null) + { + connectFlags |= 0x80; + } + + packetWriter.Write(connectFlags); + packetWriter.Write(packet.KeepAlivePeriod); + + var propertiesWriter = new MqttV500PropertiesWriter(); + if (packet.Properties != null) + { + propertiesWriter.WriteWillDelayInterval(packet.Properties.WillDelayInterval); + propertiesWriter.WriteSessionExpiryInterval(packet.Properties.SessionExpiryInterval); + propertiesWriter.WriteAuthenticationMethod(packet.Properties.AuthenticationMethod); + propertiesWriter.WriteAuthenticationData(packet.Properties.AuthenticationData); + propertiesWriter.WriteRequestProblemInformation(packet.Properties.RequestProblemInformation); + propertiesWriter.WriteRequestResponseInformation(packet.Properties.RequestResponseInformation); + propertiesWriter.WriteReceiveMaximum(packet.Properties.ReceiveMaximum); + propertiesWriter.WriteTopicAlias(packet.Properties.TopicAliasMaximum); + propertiesWriter.WriteMaximumPacketSize(packet.Properties.MaximumPacketSize); + propertiesWriter.WriteUserProperties(packet.Properties.UserProperties); + } + + propertiesWriter.WriteToPacket(packetWriter); + + packetWriter.WriteWithLengthPrefix(packet.ClientId); + + if (packet.WillMessage != null) + { + packetWriter.WriteWithLengthPrefix(packet.WillMessage.Topic); + packetWriter.WriteWithLengthPrefix(packet.WillMessage.Payload); + } + + if (packet.Username != null) + { + packetWriter.WriteWithLengthPrefix(packet.Username); + } + + if (packet.Password != null) + { + packetWriter.WriteWithLengthPrefix(packet.Password); + } + + return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Connect); + } + + private static byte EncodeConnAckPacket(MqttConnAckPacket packet, MqttPacketWriter packetWriter) + { + if (packet == null) throw new ArgumentNullException(nameof(packet)); + if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter)); + + if (!packet.ReasonCode.HasValue) + { + ThrowReasonCodeNotSetException(); + } + + byte connectAcknowledgeFlags = 0x0; + if (packet.IsSessionPresent) + { + connectAcknowledgeFlags |= 0x1; + } + + packetWriter.Write(connectAcknowledgeFlags); + packetWriter.Write((byte)packet.ReasonCode.Value); + + var propertiesWriter = new MqttV500PropertiesWriter(); + if (packet.Properties != null) + { + propertiesWriter.WriteSessionExpiryInterval(packet.Properties.SessionExpiryInterval); + propertiesWriter.WriteAuthenticationMethod(packet.Properties.AuthenticationMethod); + propertiesWriter.WriteAuthenticationData(packet.Properties.AuthenticationData); + propertiesWriter.WriteRetainAvailable(packet.Properties.RetainAvailable); + propertiesWriter.WriteReceiveMaximum(packet.Properties.ReceiveMaximum); + propertiesWriter.WriteAssignedClientIdentifier(packet.Properties.AssignedClientIdentifier); + propertiesWriter.WriteTopicAliasMaximum(packet.Properties.TopicAliasMaximum); + propertiesWriter.WriteReasonString(packet.Properties.ReasonString); + propertiesWriter.WriteMaximumPacketSize(packet.Properties.MaximumPacketSize); + propertiesWriter.WriteWildcardSubscriptionAvailable(packet.Properties.WildcardSubscriptionAvailable); + propertiesWriter.WriteSubscriptionIdentifiersAvailable(packet.Properties.SubscriptionIdentifiersAvailable); + propertiesWriter.WriteSharedSubscriptionAvailable(packet.Properties.SharedSubscriptionAvailable); + propertiesWriter.WriteServerKeepAlive(packet.Properties.ServerKeepAlive); + propertiesWriter.WriteResponseInformation(packet.Properties.ResponseInformation); + propertiesWriter.WriteServerReference(packet.Properties.ServerReference); + propertiesWriter.WriteUserProperties(packet.Properties.UserProperties); + } + + propertiesWriter.WriteToPacket(packetWriter); + + return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck); + } + + private static byte EncodePublishPacket(MqttPublishPacket packet, MqttPacketWriter packetWriter) + { + if (packet == null) throw new ArgumentNullException(nameof(packet)); + if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter)); + + if (packet.QualityOfServiceLevel == 0 && packet.Dup) + { + throw new MqttProtocolViolationException("Dup flag must be false for QoS 0 packets [MQTT-3.3.1-2]."); + } + + packetWriter.WriteWithLengthPrefix(packet.Topic); + + if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce) + { + if (!packet.PacketIdentifier.HasValue) + { + throw new MqttProtocolViolationException("Publish packet has no packet identifier."); + } + + packetWriter.Write(packet.PacketIdentifier.Value); + } + else + { + if (packet.PacketIdentifier > 0) + { + throw new MqttProtocolViolationException("Packet identifier must be 0 if QoS == 0 [MQTT-2.3.1-5]."); + } + } + + var propertiesWriter = new MqttV500PropertiesWriter(); + if (packet.Properties != null) + { + propertiesWriter.WritePayloadFormatIndicator(packet.Properties.PayloadFormatIndicator); + propertiesWriter.WriteMessageExpiryInterval(packet.Properties.MessageExpiryInterval); + propertiesWriter.WriteTopicAlias(packet.Properties.TopicAlias); + propertiesWriter.WriteResponseTopic(packet.Properties.ResponseTopic); + propertiesWriter.WriteCorrelationData(packet.Properties.CorrelationData); + propertiesWriter.WriteSubscriptionIdentifier(packet.Properties.SubscriptionIdentifier); + propertiesWriter.WriteContentType(packet.Properties.ContentType); + propertiesWriter.WriteUserProperties(packet.Properties.UserProperties); + } + + propertiesWriter.WriteToPacket(packetWriter); + + if (packet.Payload?.Length > 0) + { + packetWriter.Write(packet.Payload, 0, packet.Payload.Length); + } + + byte fixedHeader = 0; + + if (packet.Retain) + { + fixedHeader |= 0x01; + } + + fixedHeader |= (byte)((byte)packet.QualityOfServiceLevel << 1); + + if (packet.Dup) + { + fixedHeader |= 0x08; + } + + return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Publish, fixedHeader); + } + + private static byte EncodePubAckPacket(MqttPubAckPacket packet, MqttPacketWriter packetWriter) + { + if (packet == null) throw new ArgumentNullException(nameof(packet)); + if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter)); + + if (!packet.PacketIdentifier.HasValue) + { + throw new MqttProtocolViolationException("PubAck packet has no packet identifier."); + } + + if (!packet.ReasonCode.HasValue) + { + throw new MqttProtocolViolationException("PubAck packet must contain a connect reason."); + } + + packetWriter.Write(packet.PacketIdentifier.Value); + + var propertiesWriter = new MqttV500PropertiesWriter(); + if (packet.Properties != null) + { + propertiesWriter.WriteReasonString(packet.Properties.ReasonString); + propertiesWriter.WriteUserProperties(packet.Properties.UserProperties); + } + + if (packetWriter.Length > 0 || packet.ReasonCode.Value != MqttPubAckReasonCode.Success) + { + packetWriter.Write((byte)packet.ReasonCode.Value); + propertiesWriter.WriteToPacket(packetWriter); + } + + return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck); + } + + private static byte EncodePubRecPacket(MqttPubRecPacket packet, MqttPacketWriter packetWriter) + { + ThrowIfPacketIdentifierIsInvalid(packet); + + if (!packet.ReasonCode.HasValue) + { + ThrowReasonCodeNotSetException(); + } + + packetWriter.Write(packet.PacketIdentifier.Value); + packetWriter.Write((byte)packet.ReasonCode.Value); + + var propertiesWriter = new MqttV500PropertiesWriter(); + if (packet.Properties != null) + { + propertiesWriter.WriteReasonString(packet.Properties.ReasonString); + propertiesWriter.WriteUserProperties(packet.Properties.UserProperties); + } + + if (packetWriter.Length > 0 || packet.ReasonCode.Value != MqttPubRecReasonCode.Success) + { + packetWriter.Write((byte)packet.ReasonCode.Value); + propertiesWriter.WriteToPacket(packetWriter); + } + + return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec); + } + + private static byte EncodePubRelPacket(MqttPubRelPacket packet, MqttPacketWriter packetWriter) + { + ThrowIfPacketIdentifierIsInvalid(packet); + + if (!packet.ReasonCode.HasValue) + { + ThrowReasonCodeNotSetException(); + } + + packetWriter.Write(packet.PacketIdentifier.Value); + packetWriter.Write((byte)packet.ReasonCode.Value); + + var propertiesWriter = new MqttV500PropertiesWriter(); + if (packet.Properties != null) + { + propertiesWriter.WriteReasonString(packet.Properties.ReasonString); + propertiesWriter.WriteUserProperties(packet.Properties.UserProperties); + } + + if (packetWriter.Length > 0 || packet.ReasonCode.Value != MqttPubRelReasonCode.Success) + { + packetWriter.Write((byte)packet.ReasonCode.Value); + propertiesWriter.WriteToPacket(packetWriter); + } + + return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02); + } + + private static byte EncodePubCompPacket(MqttPubCompPacket packet, MqttPacketWriter packetWriter) + { + ThrowIfPacketIdentifierIsInvalid(packet); + + if (!packet.ReasonCode.HasValue) + { + ThrowReasonCodeNotSetException(); + } + + packetWriter.Write(packet.PacketIdentifier.Value); + packetWriter.Write((byte)packet.ReasonCode.Value); + + var propertiesWriter = new MqttV500PropertiesWriter(); + if (packet.Properties != null) + { + propertiesWriter.WriteReasonString(packet.Properties.ReasonString); + propertiesWriter.WriteUserProperties(packet.Properties.UserProperties); + } + + if (packetWriter.Length > 0 || packet.ReasonCode.Value != MqttPubCompReasonCode.Success) + { + packetWriter.Write((byte)packet.ReasonCode.Value); + propertiesWriter.WriteToPacket(packetWriter); + } + + return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp); + } + + private static byte EncodeSubscribePacket(MqttSubscribePacket packet, MqttPacketWriter packetWriter) + { + if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3]."); + + ThrowIfPacketIdentifierIsInvalid(packet); + + packetWriter.Write(packet.PacketIdentifier.Value); + + var propertiesWriter = new MqttV500PropertiesWriter(); + if (packet.Properties != null) + { + propertiesWriter.WriteSubscriptionIdentifier(packet.Properties.SubscriptionIdentifier); + propertiesWriter.WriteUserProperties(packet.Properties.UserProperties); + } + + propertiesWriter.WriteToPacket(packetWriter); + + if (packet.TopicFilters?.Count > 0) + { + foreach (var topicFilter in packet.TopicFilters) + { + packetWriter.WriteWithLengthPrefix(topicFilter.Topic); + + var options = (byte)topicFilter.QualityOfServiceLevel; + + if (topicFilter.NoLocal == true) + { + options |= 1 << 2; + } + + if (topicFilter.RetainAsPublished == true) + { + options |= 1 << 3; + } + + if (topicFilter.RetainHandling.HasValue) + { + options |= (byte)((byte)topicFilter.RetainHandling.Value << 4); + } + + packetWriter.Write(options); + } + } + + return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Subscribe, 0x02); + } + + private static byte EncodeSubAckPacket(MqttSubAckPacket packet, MqttPacketWriter packetWriter) + { + ThrowIfPacketIdentifierIsInvalid(packet); + + if (packet.ReasonCodes == null || !packet.ReasonCodes.Any()) + { + throw new MqttProtocolViolationException("At least one reason code must be set[MQTT - 3.8.3 - 3]."); + } + + packetWriter.Write(packet.PacketIdentifier.Value); + + var propertiesWriter = new MqttV500PropertiesWriter(); + if (packet.Properties != null) + { + propertiesWriter.WriteReasonString(packet.Properties.ReasonString); + propertiesWriter.WriteUserProperties(packet.Properties.UserProperties); + } + + propertiesWriter.WriteToPacket(packetWriter); + + foreach (var reasonCode in packet.ReasonCodes) + { + packetWriter.Write((byte)reasonCode); + } + + return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.SubAck); + } + + private static byte EncodeUnsubscribePacket(MqttUnsubscribePacket packet, MqttPacketWriter packetWriter) + { + if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2]."); + + ThrowIfPacketIdentifierIsInvalid(packet); + + packetWriter.Write(packet.PacketIdentifier.Value); + + var propertiesWriter = new MqttV500PropertiesWriter(); + if (packet.Properties != null) + { + propertiesWriter.WriteUserProperties(packet.Properties.UserProperties); + } + + propertiesWriter.WriteToPacket(packetWriter); + + foreach (var topicFilter in packet.TopicFilters) + { + packetWriter.WriteWithLengthPrefix(topicFilter); + } + + return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02); + } + + private static byte EncodeUnsubAckPacket(MqttUnsubAckPacket packet, MqttPacketWriter packetWriter) + { + ThrowIfPacketIdentifierIsInvalid(packet); + + if (packet.ReasonCodes == null || !packet.ReasonCodes.Any()) + { + throw new MqttProtocolViolationException("At least one reason code must be set[MQTT - 3.8.3 - 3]."); + } + + packetWriter.Write(packet.PacketIdentifier.Value); + + var propertiesWriter = new MqttV500PropertiesWriter(); + if (packet.Properties != null) + { + propertiesWriter.WriteReasonString(packet.Properties.ReasonString); + propertiesWriter.WriteUserProperties(packet.Properties.UserProperties); + } + + propertiesWriter.WriteToPacket(packetWriter); + + foreach (var reasonCode in packet.ReasonCodes) + { + packetWriter.Write((byte)reasonCode); + } + + return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck); + } + + private static byte EncodeDisconnectPacket(MqttDisconnectPacket packet, MqttPacketWriter packetWriter) + { + if (!packet.ReasonCode.HasValue) + { + ThrowReasonCodeNotSetException(); + } + + packetWriter.Write((byte)packet.ReasonCode.Value); + + var propertiesWriter = new MqttV500PropertiesWriter(); + if (packet.Properties != null) + { + propertiesWriter.WriteServerReference(packet.Properties.ServerReference); + propertiesWriter.WriteReasonString(packet.Properties.ReasonString); + propertiesWriter.WriteSessionExpiryInterval(packet.Properties.SessionExpiryInterval); + propertiesWriter.WriteUserProperties(packet.Properties.UserProperties); + } + + propertiesWriter.WriteToPacket(packetWriter); + + return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Disconnect); + } + + private static byte EncodePingReqPacket() + { + return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PingReq); + } + + private static byte EncodePingRespPacket() + { + return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PingResp); + } + + private static byte EncodeAuthPacket(MqttAuthPacket packet, MqttPacketWriter packetWriter) + { + packetWriter.Write((byte)packet.ReasonCode); + + var propertiesWriter = new MqttV500PropertiesWriter(); + if (packet.Properties != null) + { + propertiesWriter.WriteAuthenticationMethod(packet.Properties.AuthenticationMethod); + propertiesWriter.WriteAuthenticationData(packet.Properties.AuthenticationData); + propertiesWriter.WriteReasonString(packet.Properties.ReasonString); + propertiesWriter.WriteUserProperties(packet.Properties.UserProperties); + } + + propertiesWriter.WriteToPacket(packetWriter); + + return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Auth); + } + + private static void ThrowReasonCodeNotSetException() + { + throw new MqttProtocolViolationException("The ReasonCode must be set for MQTT version 5."); + } + + private static void ThrowIfPacketIdentifierIsInvalid(IMqttPacketWithIdentifier packet) + { + if (!packet.PacketIdentifier.HasValue) + { + throw new MqttProtocolViolationException($"Packet identifier is not set for {packet.GetType().Name}."); + } + } + } +} diff --git a/Source/MQTTnet/Formatter/V5/MqttV500PacketFormatter.cs b/Source/MQTTnet/Formatter/V5/MqttV500PacketFormatter.cs new file mode 100644 index 0000000..d397985 --- /dev/null +++ b/Source/MQTTnet/Formatter/V5/MqttV500PacketFormatter.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using MQTTnet.Adapter; +using MQTTnet.Packets; + +namespace MQTTnet.Formatter.V5 +{ + public class MqttV500PacketFormatter : IMqttPacketFormatter + { + private readonly MqttV500PacketEncoder _encoder = new MqttV500PacketEncoder(); + private readonly MqttV500PacketDecoder _decoder = new MqttV500PacketDecoder(); + + public IMqttDataConverter DataConverter { get; } = new MqttV500DataConverter(); + + public ArraySegment Encode(MqttBasePacket mqttPacket) + { + if (mqttPacket == null) throw new ArgumentNullException(nameof(mqttPacket)); + + return _encoder.Encode(mqttPacket); + } + + public MqttBasePacket Decode(ReceivedMqttPacket receivedMqttPacket) + { + if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket)); + + return _decoder.Decode(receivedMqttPacket); + } + + public void FreeBuffer() + { + _encoder.FreeBuffer(); + } + } +} diff --git a/Source/MQTTnet/Formatter/V5/MqttV500PropertiesReader.cs b/Source/MQTTnet/Formatter/V5/MqttV500PropertiesReader.cs new file mode 100644 index 0000000..c7f28dd --- /dev/null +++ b/Source/MQTTnet/Formatter/V5/MqttV500PropertiesReader.cs @@ -0,0 +1,196 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using MQTTnet.Exceptions; +using MQTTnet.Packets; +using MQTTnet.Protocol; + +namespace MQTTnet.Formatter.V5 +{ + public class MqttV500PropertiesReader + { + private readonly MqttPacketBodyReader _body; + private readonly uint _length; + private readonly ulong _targetOffset; + + public MqttV500PropertiesReader(MqttPacketBodyReader body) + { + _body = body ?? throw new ArgumentNullException(nameof(body)); + + if (!body.EndOfStream) + { + _length = body.ReadVariableLengthInteger(); + } + + _targetOffset = body.Offset + _length; + } + + public MqttPropertyId CurrentPropertyId { get; private set; } + + public bool MoveNext() + { + if (_length == 0) + { + return false; + } + + if (_body.Offset >= _targetOffset) + { + return false; + } + + CurrentPropertyId = (MqttPropertyId)_body.ReadByte(); + return true; + } + + public void FillUserProperties(List userProperties) + { + if (userProperties == null) throw new ArgumentNullException(nameof(userProperties)); + + var userPropertiesLength = _body.ReadVariableLengthInteger(); + if (userPropertiesLength == 0) + { + return; + } + + var targetPosition = _body.Offset + userPropertiesLength; + while (_body.Offset < targetPosition) + { + var name = _body.ReadStringWithLengthPrefix(); + var value = _body.ReadStringWithLengthPrefix(); + + userProperties.Add(new MqttUserProperty(name, value)); + } + } + + public string ReadReasonString() + { + return _body.ReadStringWithLengthPrefix(); + } + + public string ReadAuthenticationMethod() + { + return _body.ReadStringWithLengthPrefix(); + } + + public byte[] ReadAuthenticationData() + { + return _body.ReadWithLengthPrefix().ToArray(); + } + + public bool? ReadRetainAvailable() + { + return _body.ReadBoolean(); + } + + public uint? ReadSessionExpiryInterval() + { + return _body.ReadFourByteInteger(); + } + + public ushort? ReadReceiveMaximum() + { + return _body.ReadTwoByteInteger(); + } + + public string ReadAssignedClientIdentifier() + { + return _body.ReadStringWithLengthPrefix(); + } + + public string ReadServerReference() + { + return _body.ReadStringWithLengthPrefix(); + } + + public ushort? ReadTopicAliasMaximum() + { + return _body.ReadTwoByteInteger(); + } + + public uint? ReadMaximumPacketSize() + { + return _body.ReadFourByteInteger(); + } + + public ushort? ReadServerKeepAlive() + { + return _body.ReadTwoByteInteger(); + } + + public string ReadResponseInformation() + { + return _body.ReadStringWithLengthPrefix(); + } + + public bool? ReadSharedSubscriptionAvailable() + { + return _body.ReadBoolean(); + } + + public bool? ReadSubscriptionIdentifiersAvailable() + { + return _body.ReadBoolean(); + } + + public bool? ReadWildcardSubscriptionAvailable() + { + return _body.ReadBoolean(); + } + + public uint? ReadSubscriptionIdentifier() + { + return _body.ReadVariableLengthInteger(); + } + + public MqttPayloadFormatIndicator? ReadPayloadFormatIndicator() + { + return (MqttPayloadFormatIndicator)_body.ReadByte(); + } + + public uint? ReadMessageExpiryInterval() + { + return _body.ReadFourByteInteger(); + } + + public ushort? ReadTopicAlias() + { + return _body.ReadTwoByteInteger(); + } + + public string ReadResponseTopic() + { + return _body.ReadStringWithLengthPrefix(); + } + + public byte[] ReadCorrelationData() + { + return _body.ReadWithLengthPrefix().ToArray(); + } + + public string ReadContentType() + { + return _body.ReadStringWithLengthPrefix(); + } + + public uint? ReadWillDelayInterval() + { + return _body.ReadFourByteInteger(); + } + + public bool? RequestResponseInformation() + { + return _body.ReadBoolean(); + } + + public bool? RequestProblemInformation() + { + return _body.ReadBoolean(); + } + + public void ThrowInvalidPropertyIdException(Type type) + { + throw new MqttProtocolViolationException($"Property ID '{CurrentPropertyId}' is not supported for package type '{type.Name}'."); + } + } +} diff --git a/Source/MQTTnet/Formatter/V5/MqttV500PropertiesWriter.cs b/Source/MQTTnet/Formatter/V5/MqttV500PropertiesWriter.cs new file mode 100644 index 0000000..e10790c --- /dev/null +++ b/Source/MQTTnet/Formatter/V5/MqttV500PropertiesWriter.cs @@ -0,0 +1,238 @@ +using System; +using System.Collections.Generic; +using MQTTnet.Packets; +using MQTTnet.Protocol; + +namespace MQTTnet.Formatter.V5 +{ + public class MqttV500PropertiesWriter + { + private readonly MqttPacketWriter _packetWriter = new MqttPacketWriter(); + + public void WriteUserProperties(List userProperties) + { + if (userProperties == null || userProperties.Count == 0) + { + return; + } + + var propertyWriter = new MqttPacketWriter(); + foreach (var property in userProperties) + { + propertyWriter.WriteWithLengthPrefix(property.Name); + propertyWriter.WriteWithLengthPrefix(property.Value); + } + + _packetWriter.Write((byte)MqttPropertyId.UserProperty); + _packetWriter.WriteVariableLengthInteger((uint)propertyWriter.Length); + _packetWriter.Write(propertyWriter); + } + + public void WriteCorrelationData(byte[] value) + { + Write(MqttPropertyId.CorrelationData, value); + } + + public void WriteAuthenticationData(byte[] value) + { + Write(MqttPropertyId.AuthenticationData, value); + } + + public void WriteReasonString(string value) + { + Write(MqttPropertyId.ReasonString, value); + } + + public void WriteResponseTopic(string value) + { + Write(MqttPropertyId.ResponseTopic, value); + } + + public void WriteContentType(string value) + { + Write(MqttPropertyId.ContentType, value); + } + + public void WriteServerReference(string value) + { + Write(MqttPropertyId.ServerReference, value); + } + + public void WriteAuthenticationMethod(string value) + { + Write(MqttPropertyId.AuthenticationMethod, value); + } + + public void WriteToPacket(MqttPacketWriter packetWriter) + { + if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter)); + + packetWriter.WriteVariableLengthInteger((uint)_packetWriter.Length); + packetWriter.Write(_packetWriter); + } + + public void WriteSessionExpiryInterval(uint? value) + { + WriteAsFourByteInteger(MqttPropertyId.SessionExpiryInterval, value); + } + + public void WriteSubscriptionIdentifier(uint? value) + { + WriteAsVariableLengthInteger(MqttPropertyId.SubscriptionIdentifier, value); + } + + public void WriteTopicAlias(ushort? value) + { + Write(MqttPropertyId.TopicAlias, value); + } + + public void WriteMessageExpiryInterval(uint? value) + { + WriteAsFourByteInteger(MqttPropertyId.MessageExpiryInterval, value); + } + + public void WritePayloadFormatIndicator(MqttPayloadFormatIndicator? value) + { + if (!value.HasValue) + { + return; + } + + Write(MqttPropertyId.PayloadFormatIndicator, (byte)value.Value); + } + + public void WriteWillDelayInterval(uint? value) + { + WriteAsFourByteInteger(MqttPropertyId.WillDelayInterval, value); + } + + public void WriteRequestProblemInformation(bool? value) + { + Write(MqttPropertyId.RequestProblemInformation, value); + } + + public void WriteRequestResponseInformation(bool? value) + { + Write(MqttPropertyId.RequestResponseInformation, value); + } + + public void WriteReceiveMaximum(ushort? value) + { + Write(MqttPropertyId.RequestResponseInformation, value); + } + + public void WriteMaximumPacketSize(uint? value) + { + WriteAsFourByteInteger(MqttPropertyId.MaximumPacketSize, value); + } + + public void WriteRetainAvailable(bool? value) + { + Write(MqttPropertyId.RetainAvailable, value); + } + + public void WriteAssignedClientIdentifier(string value) + { + Write(MqttPropertyId.AssignedClientIdentifer, value); + } + + public void WriteTopicAliasMaximum(ushort? value) + { + Write(MqttPropertyId.TopicAliasMaximum, value); + } + + public void WriteWildcardSubscriptionAvailable(bool? value) + { + Write(MqttPropertyId.WildcardSubscriptionAvailable, value); + } + + public void WriteSubscriptionIdentifiersAvailable(bool? value) + { + Write(MqttPropertyId.SubscriptionIdentifiersAvailable, value); + } + + public void WriteSharedSubscriptionAvailable(bool? value) + { + Write(MqttPropertyId.SharedSubscriptionAvailable, value); + } + + public void WriteServerKeepAlive(ushort? value) + { + Write(MqttPropertyId.ServerKeepAlive, value); + } + + public void WriteResponseInformation(string value) + { + Write(MqttPropertyId.ResponseInformation, value); + } + + private void Write(MqttPropertyId id, bool? value) + { + if (!value.HasValue) + { + return; + } + + _packetWriter.Write((byte)id); + _packetWriter.Write(value.Value ? (byte)0x1 : (byte)0x0); + } + + private void Write(MqttPropertyId id, ushort? value) + { + if (!value.HasValue) + { + return; + } + + _packetWriter.Write((byte)id); + _packetWriter.Write(value.Value); + } + + private void WriteAsVariableLengthInteger(MqttPropertyId id, uint? value) + { + if (!value.HasValue) + { + return; + } + + _packetWriter.Write((byte)id); + _packetWriter.WriteVariableLengthInteger(value.Value); + } + + private void WriteAsFourByteInteger(MqttPropertyId id, uint? value) + { + if (!value.HasValue) + { + return; + } + + _packetWriter.Write((byte)id); + _packetWriter.Write((byte)(value.Value >> 24)); + _packetWriter.Write((byte)(value.Value >> 16)); + _packetWriter.Write((byte)(value.Value >> 8)); + _packetWriter.Write((byte)value.Value); + } + + private void Write(MqttPropertyId id, string value) + { + if (value == null) + { + return; + } + + _packetWriter.Write((byte)id); + _packetWriter.WriteWithLengthPrefix(value); + } + + private void Write(MqttPropertyId id, byte[] value) + { + if (value == null) + { + return; + } + + _packetWriter.Write((byte)id); + _packetWriter.WriteWithLengthPrefix(value); + } + } +} diff --git a/Source/MQTTnet/Formatter/V500/MqttV500PacketDecoder.cs b/Source/MQTTnet/Formatter/V500/MqttV500PacketDecoder.cs deleted file mode 100644 index c0b3531..0000000 --- a/Source/MQTTnet/Formatter/V500/MqttV500PacketDecoder.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using MQTTnet.Exceptions; -using MQTTnet.Packets; -using MQTTnet.Protocol; - -namespace MQTTnet.Formatter.V500 -{ - public class MqttV500PacketDecoder - { - public MqttBasePacket DecodeConnAckPacket(MqttPacketBodyReader body) - { - if (body == null) throw new ArgumentNullException(nameof(body)); - - ThrowIfBodyIsEmpty(body); - - var packet = new MqttConnAckPacket(); - - var acknowledgeFlags = body.ReadByte(); - - packet.IsSessionPresent = (acknowledgeFlags & 0x1) > 0; - packet.ConnectReturnCode = (MqttConnectReturnCode)body.ReadByte(); - packet.ReasonCode = (MqttConnectReasonCode)body.ReadByte(); - - var propertiesLength = body.ReadVariableLengthInteger(); - if (propertiesLength > 0) - { - packet.Properties = new MqttConnAckPacketProperties(); - - while (!body.EndOfStream) - { - var propertyID = (MqttPropertyID)body.ReadByte(); - - if (propertyID == MqttPropertyID.SessionExpiryInterval) - { - packet.Properties.SessionExpiryInterval = body.ReadFourByteInteger(); - } - else if (propertyID == MqttPropertyID.AuthenticationMethod) - { - packet.Properties.AuthenticationMethod = body.ReadStringWithLengthPrefix(); - } - else if (propertyID == MqttPropertyID.AuthenticationData) - { - packet.Properties.AuthenticationData = body.ReadWithLengthPrefix().ToArray(); - } - else if (propertyID == MqttPropertyID.RetainAvailable) - { - packet.Properties.RetainAvailable = body.ReadBoolean(); - } - else if (propertyID == MqttPropertyID.ReceiveMaximum) - { - packet.Properties.ReceiveMaximum = body.ReadTwoByteInteger(); - } - else if (propertyID == MqttPropertyID.AssignedClientIdentifer) - { - packet.Properties.AssignedClientIdentifier = body.ReadStringWithLengthPrefix(); - } - else if (propertyID == MqttPropertyID.TopicAliasMaximum) - { - packet.Properties.TopicAliasMaximum = body.ReadTwoByteInteger(); - } - else if (propertyID == MqttPropertyID.ReasonString) - { - packet.Properties.ReasonString = body.ReadStringWithLengthPrefix(); - } - else if (propertyID == MqttPropertyID.MaximumPacketSize) - { - packet.Properties.MaximumPacketSize = body.ReadFourByteInteger(); - } - else if (propertyID == MqttPropertyID.WildcardSubscriptionAvailable) - { - packet.Properties.WildcardSubscriptionAvailable = body.ReadBoolean(); - } - else if (propertyID == MqttPropertyID.SubscriptionIdentifierAvailable) - { - packet.Properties.SubscriptionIdentifiersAvailable = body.ReadBoolean(); - } - else if (propertyID == MqttPropertyID.SharedSubscriptionAvailable) - { - packet.Properties.SharedSubscriptionAvailable = body.ReadBoolean(); - } - else if (propertyID == MqttPropertyID.ServerKeepAlive) - { - packet.Properties.ServerKeepAlive = body.ReadTwoByteInteger(); - } - else if (propertyID == MqttPropertyID.ResponseInformation) - { - packet.Properties.ResponseInformation = body.ReadStringWithLengthPrefix(); - } - else if (propertyID == MqttPropertyID.ServerReference) - { - packet.Properties.ServerReference = body.ReadStringWithLengthPrefix(); - } - else if (propertyID == MqttPropertyID.UserProperty) - { - packet.Properties.UserProperties = ReadUserProperties(body); - } - } - } - - return packet; - } - - // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local - private static void ThrowIfBodyIsEmpty(MqttPacketBodyReader body) - { - if (body == null || body.Length == 0) - { - throw new MqttProtocolViolationException("Data from the body is required but not present."); - } - } - - private static List ReadUserProperties(MqttPacketBodyReader reader) - { - var userPropertiesLength = reader.ReadVariableLengthInteger(); - if (userPropertiesLength == 0) - { - return new List(); - } - - var userProperties = new List(); - var targetPosition = reader.Offset + userPropertiesLength; - while (reader.Offset < targetPosition) - { - var name = reader.ReadStringWithLengthPrefix(); - var value = reader.ReadStringWithLengthPrefix(); - - userProperties.Add(new MqttUserProperty(name, value)); - } - - return userProperties; - } - } -} diff --git a/Source/MQTTnet/Formatter/V500/MqttV500PacketEncoder.cs b/Source/MQTTnet/Formatter/V500/MqttV500PacketEncoder.cs deleted file mode 100644 index cb4d2a6..0000000 --- a/Source/MQTTnet/Formatter/V500/MqttV500PacketEncoder.cs +++ /dev/null @@ -1,223 +0,0 @@ -using System; -using MQTTnet.Exceptions; -using MQTTnet.Packets; -using MQTTnet.Protocol; - -namespace MQTTnet.Formatter.V500 -{ - public class MqttV500PacketEncoder - { - public byte EncodeConnectPacket(MqttConnectPacket packet, MqttPacketWriter packetWriter) - { - if (packet == null) throw new ArgumentNullException(nameof(packet)); - if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter)); - - if (string.IsNullOrEmpty(packet.ClientId) && !packet.CleanSession) - { - throw new MqttProtocolViolationException("CleanSession must be set if ClientId is empty [MQTT-3.1.3-7]."); - } - - packetWriter.WriteWithLengthPrefix("MQTT"); - packetWriter.Write(5); // [3.1.2.2 Protocol Version] - - byte connectFlags = 0x0; - if (packet.CleanSession) - { - connectFlags |= 0x2; - } - - if (packet.WillMessage != null) - { - connectFlags |= 0x4; - connectFlags |= (byte)((byte)packet.WillMessage.QualityOfServiceLevel << 3); - - if (packet.WillMessage.Retain) - { - connectFlags |= 0x20; - } - } - - if (packet.Password != null && packet.Username == null) - { - throw new MqttProtocolViolationException("If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22]."); - } - - if (packet.Password != null) - { - connectFlags |= 0x40; - } - - if (packet.Username != null) - { - connectFlags |= 0x80; - } - - packetWriter.Write(connectFlags); - packetWriter.Write(packet.KeepAlivePeriod); - - var propertiesWriter = new MqttV500PropertiesWriter(); - if (packet.Properties != null) - { - propertiesWriter.WriteAsFourByteInteger(MqttPropertyID.WillDelayInterval, packet.Properties.WillDelayInterval); - propertiesWriter.WriteAsFourByteInteger(MqttPropertyID.SessionExpiryInterval, packet.Properties.SessionExpiryInterval); - propertiesWriter.Write(MqttPropertyID.AuthenticationMethod, packet.Properties.AuthenticationMethod); - propertiesWriter.Write(MqttPropertyID.AuthenticationData, packet.Properties.AuthenticationData); - propertiesWriter.Write(MqttPropertyID.RequestProblemInformation, packet.Properties.RequestProblemInformation); - propertiesWriter.Write(MqttPropertyID.RequestResponseInformation, packet.Properties.RequestResponseInformation); - propertiesWriter.Write(MqttPropertyID.ReceiveMaximum, packet.Properties.ReceiveMaximum); - propertiesWriter.Write(MqttPropertyID.TopicAlias, packet.Properties.TopicAliasMaximum); - propertiesWriter.WriteAsFourByteInteger(MqttPropertyID.MaximumPacketSize, packet.Properties.MaximumPacketSize); - propertiesWriter.WriteUserProperties(packet.Properties.UserProperties); - propertiesWriter.WriteToPacket(packetWriter); - } - - packetWriter.WriteWithLengthPrefix(packet.ClientId); - - if (packet.WillMessage != null) - { - packetWriter.WriteWithLengthPrefix(packet.WillMessage.Topic); - packetWriter.WriteWithLengthPrefix(packet.WillMessage.Payload); - } - - if (packet.Username != null) - { - packetWriter.WriteWithLengthPrefix(packet.Username); - } - - if (packet.Password != null) - { - packetWriter.WriteWithLengthPrefix(packet.Password); - } - - return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Connect); - } - - public byte EncodeConnAckPacket(MqttConnAckPacket packet, MqttPacketWriter packetWriter) - { - if (packet == null) throw new ArgumentNullException(nameof(packet)); - if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter)); - - if (!packet.ReasonCode.HasValue) - { - throw new MqttProtocolViolationException("The ReasonCode must be set for MQTT version 5."); - } - - byte connectAcknowledgeFlags = 0x0; - if (packet.IsSessionPresent) - { - connectAcknowledgeFlags |= 0x1; - } - - packetWriter.Write(connectAcknowledgeFlags); - packetWriter.Write((byte)packet.ConnectReturnCode); - packetWriter.Write((byte)packet.ReasonCode.Value); - - var propertiesWriter = new MqttV500PropertiesWriter(); - if (packet.Properties != null) - { - propertiesWriter.WriteAsFourByteInteger(MqttPropertyID.SessionExpiryInterval, packet.Properties.SessionExpiryInterval); - propertiesWriter.Write(MqttPropertyID.AuthenticationMethod, packet.Properties.AuthenticationMethod); - propertiesWriter.Write(MqttPropertyID.AuthenticationData, packet.Properties.AuthenticationData); - propertiesWriter.Write(MqttPropertyID.RetainAvailable, packet.Properties.RetainAvailable); - propertiesWriter.Write(MqttPropertyID.ReceiveMaximum, packet.Properties.ReceiveMaximum); - propertiesWriter.Write(MqttPropertyID.AssignedClientIdentifer, packet.Properties.AssignedClientIdentifier); - propertiesWriter.Write(MqttPropertyID.TopicAliasMaximum, packet.Properties.TopicAliasMaximum); - propertiesWriter.Write(MqttPropertyID.ReasonString, packet.Properties.ReasonString); - propertiesWriter.WriteAsFourByteInteger(MqttPropertyID.MaximumPacketSize, packet.Properties.MaximumPacketSize); - propertiesWriter.Write(MqttPropertyID.WildcardSubscriptionAvailable, packet.Properties.WildcardSubscriptionAvailable); - propertiesWriter.Write(MqttPropertyID.SubscriptionIdentifierAvailable, packet.Properties.SubscriptionIdentifiersAvailable); - propertiesWriter.Write(MqttPropertyID.SharedSubscriptionAvailable, packet.Properties.SharedSubscriptionAvailable); - propertiesWriter.Write(MqttPropertyID.ServerKeepAlive, packet.Properties.ServerKeepAlive); - propertiesWriter.Write(MqttPropertyID.ResponseInformation, packet.Properties.ResponseInformation); - propertiesWriter.Write(MqttPropertyID.ServerReference, packet.Properties.ServerReference); - propertiesWriter.WriteUserProperties(packet.Properties.UserProperties); - propertiesWriter.WriteToPacket(packetWriter); - } - - return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck); - } - - public byte EncodePublishPacket(MqttPublishPacket packet, MqttPacketWriter packetWriter) - { - if (packet == null) throw new ArgumentNullException(nameof(packet)); - if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter)); - - if (packet.QualityOfServiceLevel == 0 && packet.Dup) - { - throw new MqttProtocolViolationException("Dup flag must be false for QoS 0 packets [MQTT-3.3.1-2]."); - } - - packetWriter.WriteWithLengthPrefix(packet.Topic); - - if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce) - { - if (!packet.PacketIdentifier.HasValue) - { - throw new MqttProtocolViolationException("Publish packet has no packet identifier."); - } - - packetWriter.Write(packet.PacketIdentifier.Value); - } - else - { - if (packet.PacketIdentifier > 0) - { - throw new MqttProtocolViolationException("Packet identifier must be empty if QoS == 0 [MQTT-2.3.1-5]."); - } - } - - var propertiesWriter = new MqttV500PropertiesWriter(); - propertiesWriter.Write(MqttPropertyID.PayloadFormatIndicator, packet.Properties.PayloadFormatIndicator); - propertiesWriter.WriteAsFourByteInteger(MqttPropertyID.MessageExpiryInterval, packet.Properties.MessageExpiryInterval); - propertiesWriter.Write(MqttPropertyID.TopicAlias, packet.Properties.TopicAlias); - propertiesWriter.Write(MqttPropertyID.ResponseTopic, packet.Properties.ResponseTopic); - propertiesWriter.Write(MqttPropertyID.CorrelationData, packet.Properties.CorrelationData); - propertiesWriter.WriteAsVariableLengthInteger(MqttPropertyID.SubscriptionIdentifier, packet.Properties.SubscriptionIdentifier); - propertiesWriter.Write(MqttPropertyID.ContentType, packet.Properties.ContentType); - propertiesWriter.WriteUserProperties(packet.Properties.UserProperties); - propertiesWriter.WriteToPacket(packetWriter); - - if (packet.Payload?.Length > 0) - { - packetWriter.Write(packet.Payload, 0, packet.Payload.Length); - } - - byte fixedHeader = 0; - - if (packet.Retain) - { - fixedHeader |= 0x01; - } - - fixedHeader |= (byte)((byte)packet.QualityOfServiceLevel << 1); - - if (packet.Dup) - { - fixedHeader |= 0x08; - } - - return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Publish, fixedHeader); - } - - public byte EncodePubAckPacket(MqttPubAckPacket packet, MqttPacketWriter packetWriter) - { - if (packet == null) throw new ArgumentNullException(nameof(packet)); - if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter)); - - if (!packet.PacketIdentifier.HasValue) - { - throw new MqttProtocolViolationException("PubAck packet has no packet identifier."); - } - - if (!packet.ReasonCode.HasValue) - { - throw new MqttProtocolViolationException("PubAck packet must contain a connect reason."); - } - - packetWriter.Write(packet.PacketIdentifier.Value); - packetWriter.Write((byte)packet.ReasonCode.Value); - - return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck); - } - } -} diff --git a/Source/MQTTnet/Formatter/V500/MqttV500PacketFormatter.cs b/Source/MQTTnet/Formatter/V500/MqttV500PacketFormatter.cs deleted file mode 100644 index f815129..0000000 --- a/Source/MQTTnet/Formatter/V500/MqttV500PacketFormatter.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; -using MQTTnet.Formatter.V311; -using MQTTnet.Packets; - -namespace MQTTnet.Formatter.V500 -{ - public class MqttV500PacketFormatter : MqttV311PacketFormatter - { - private readonly MqttV500PacketEncoder _encoder = new MqttV500PacketEncoder(); - private readonly MqttV500PacketDecoder _decoder = new MqttV500PacketDecoder(); - - public override MqttPublishPacket ConvertApplicationMessageToPublishPacket(MqttApplicationMessage applicationMessage) - { - if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage)); - - return new MqttPublishPacket - { - Topic = applicationMessage.Topic, - Payload = applicationMessage.Payload, - QualityOfServiceLevel = applicationMessage.QualityOfServiceLevel, - Retain = applicationMessage.Retain, - Dup = false, - Properties = new MqttPublishPacketProperties - { - UserProperties = new List(applicationMessage.UserProperties) - } - }; - } - - protected override byte SerializeConnectPacket(MqttConnectPacket packet, MqttPacketWriter packetWriter) - { - return _encoder.EncodeConnectPacket(packet, packetWriter); - } - - protected override byte SerializeConnAckPacket(MqttConnAckPacket packet, MqttPacketWriter packetWriter) - { - return _encoder.EncodeConnAckPacket(packet, packetWriter); - } - - protected override MqttBasePacket DeserializeConnAckPacket(MqttPacketBodyReader body) - { - return _decoder.DecodeConnAckPacket(body); - } - - protected override byte SerializePublishPacket(MqttPublishPacket packet, MqttPacketWriter packetWriter) - { - return _encoder.EncodePublishPacket(packet, packetWriter); - } - - protected override byte SerializePubAckPacket(MqttPubAckPacket packet, MqttPacketWriter packetWriter) - { - return _encoder.EncodePubAckPacket(packet, packetWriter); - } - } -} diff --git a/Source/MQTTnet/Formatter/V500/MqttV500PropertiesWriter.cs b/Source/MQTTnet/Formatter/V500/MqttV500PropertiesWriter.cs deleted file mode 100644 index 1099227..0000000 --- a/Source/MQTTnet/Formatter/V500/MqttV500PropertiesWriter.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System; -using System.Collections.Generic; -using MQTTnet.Packets; -using MQTTnet.Protocol; - -namespace MQTTnet.Formatter.V500 -{ - public class MqttV500PropertiesWriter - { - private readonly MqttPacketWriter _packetWriter = new MqttPacketWriter(); - - public void Write(MqttPropertyID id, bool? value) - { - if (!value.HasValue) - { - return; - } - - _packetWriter.Write((byte)id); - _packetWriter.Write(value.Value ? (byte)0x1 : (byte)0x0); - } - - public void Write(MqttPropertyID id, ushort? value) - { - if (!value.HasValue) - { - return; - } - - _packetWriter.Write((byte)id); - _packetWriter.Write(value.Value); - } - - public void WriteAsVariableLengthInteger(MqttPropertyID id, uint? value) - { - if (!value.HasValue) - { - return; - } - - _packetWriter.Write((byte)id); - _packetWriter.WriteVariableLengthInteger(value.Value); - } - - public void WriteAsFourByteInteger(MqttPropertyID id, uint? value) - { - if (!value.HasValue) - { - return; - } - - _packetWriter.Write((byte)id); - _packetWriter.Write((byte)(value.Value >> 24)); - _packetWriter.Write((byte)(value.Value >> 16)); - _packetWriter.Write((byte)(value.Value >> 8)); - _packetWriter.Write((byte)value.Value); - } - - public void Write(MqttPropertyID id, string value) - { - if (value == null) - { - return; - } - - _packetWriter.Write((byte)id); - _packetWriter.WriteWithLengthPrefix(value); - } - - public void Write(MqttPropertyID id, byte[] value) - { - if (value == null) - { - return; - } - - _packetWriter.Write((byte)id); - _packetWriter.WriteWithLengthPrefix(value); - } - - public void WriteUserProperties(List userProperties) - { - if (userProperties == null || userProperties.Count == 0) - { - return; - } - - var propertyWriter = new MqttPacketWriter(); - foreach (var property in userProperties) - { - propertyWriter.WriteWithLengthPrefix(property.Name); - propertyWriter.WriteWithLengthPrefix(property.Value); - } - - _packetWriter.Write((byte)MqttPropertyID.UserProperty); - _packetWriter.WriteVariableLengthInteger((uint)propertyWriter.Length); - _packetWriter.Write(propertyWriter); - } - - public void WriteToPacket(MqttPacketWriter packetWriter) - { - if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter)); - - packetWriter.WriteVariableLengthInteger((uint)_packetWriter.Length); - packetWriter.Write(packetWriter); - } - } -} diff --git a/Source/MQTTnet/IApplicationMessagePublisher.cs b/Source/MQTTnet/IApplicationMessagePublisher.cs index c47893a..a7c0d4a 100644 --- a/Source/MQTTnet/IApplicationMessagePublisher.cs +++ b/Source/MQTTnet/IApplicationMessagePublisher.cs @@ -1,9 +1,10 @@ using System.Threading.Tasks; +using MQTTnet.Client.Publishing; namespace MQTTnet { public interface IApplicationMessagePublisher { - Task PublishAsync(MqttApplicationMessage applicationMessage); + Task PublishAsync(MqttApplicationMessage applicationMessage); } } diff --git a/Source/MQTTnet/Implementations/MqttClientAdapterFactory.cs b/Source/MQTTnet/Implementations/MqttClientAdapterFactory.cs index 4f88c74..0f5df6f 100644 --- a/Source/MQTTnet/Implementations/MqttClientAdapterFactory.cs +++ b/Source/MQTTnet/Implementations/MqttClientAdapterFactory.cs @@ -1,6 +1,7 @@ using System; using MQTTnet.Adapter; using MQTTnet.Client; +using MQTTnet.Client.Options; using MQTTnet.Diagnostics; using MQTTnet.Formatter; diff --git a/Source/MQTTnet/Implementations/MqttTcpChannel.Uwp.cs b/Source/MQTTnet/Implementations/MqttTcpChannel.Uwp.cs index 6da9da2..fa20b08 100644 --- a/Source/MQTTnet/Implementations/MqttTcpChannel.Uwp.cs +++ b/Source/MQTTnet/Implementations/MqttTcpChannel.Uwp.cs @@ -10,7 +10,7 @@ using Windows.Networking; using Windows.Networking.Sockets; using Windows.Security.Cryptography.Certificates; using MQTTnet.Channel; -using MQTTnet.Client; +using MQTTnet.Client.Options; using MQTTnet.Server; namespace MQTTnet.Implementations diff --git a/Source/MQTTnet/Implementations/MqttTcpChannel.cs b/Source/MQTTnet/Implementations/MqttTcpChannel.cs index 58eece6..978f8b6 100644 --- a/Source/MQTTnet/Implementations/MqttTcpChannel.cs +++ b/Source/MQTTnet/Implementations/MqttTcpChannel.cs @@ -8,7 +8,7 @@ using System.IO; using System.Linq; using System.Threading; using MQTTnet.Channel; -using MQTTnet.Client; +using MQTTnet.Client.Options; namespace MQTTnet.Implementations { diff --git a/Source/MQTTnet/Implementations/MqttWebSocketChannel.cs b/Source/MQTTnet/Implementations/MqttWebSocketChannel.cs index 53173d2..c6926dd 100644 --- a/Source/MQTTnet/Implementations/MqttWebSocketChannel.cs +++ b/Source/MQTTnet/Implementations/MqttWebSocketChannel.cs @@ -5,7 +5,7 @@ using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; using MQTTnet.Channel; -using MQTTnet.Client; +using MQTTnet.Client.Options; namespace MQTTnet.Implementations { @@ -155,9 +155,7 @@ namespace MQTTnet.Implementations if (!string.IsNullOrEmpty(_options.ProxyOptions.Username) && !string.IsNullOrEmpty(_options.ProxyOptions.Password)) { - var credentials = - new NetworkCredential(_options.ProxyOptions.Username, _options.ProxyOptions.Password, _options.ProxyOptions.Domain); - + var credentials = new NetworkCredential(_options.ProxyOptions.Username, _options.ProxyOptions.Password, _options.ProxyOptions.Domain); return new WebProxy(proxyUri, _options.ProxyOptions.BypassOnLocal, _options.ProxyOptions.BypassList, credentials); } diff --git a/Source/MQTTnet/Internal/MqttApplicationMessageExtensions.cs b/Source/MQTTnet/Internal/MqttApplicationMessageExtensions.cs deleted file mode 100644 index 7eebda6..0000000 --- a/Source/MQTTnet/Internal/MqttApplicationMessageExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using MQTTnet.Packets; - -namespace MQTTnet.Internal -{ - public static class MqttApplicationMessageExtensions - { - public static MqttApplicationMessage ToApplicationMessage(this MqttPublishPacket publishPacket) - { - return new MqttApplicationMessage - { - Topic = publishPacket.Topic, - Payload = publishPacket.Payload, - QualityOfServiceLevel = publishPacket.QualityOfServiceLevel, - Retain = publishPacket.Retain - }; - } - } -} diff --git a/Source/MQTTnet/MqttApplicationMessage.cs b/Source/MQTTnet/MqttApplicationMessage.cs index 54135fb..4996c22 100644 --- a/Source/MQTTnet/MqttApplicationMessage.cs +++ b/Source/MQTTnet/MqttApplicationMessage.cs @@ -12,8 +12,22 @@ namespace MQTTnet public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; } - public List UserProperties { get; set; } - public bool Retain { get; set; } + + public List UserProperties { get; set; } = new List(); + + public string ContentType { get; set; } + + public string ResponseTopic { get; set; } + + public MqttPayloadFormatIndicator? PayloadFormatIndicator { get; set; } + + public uint? MessageExpiryInterval { get; set; } + + public ushort? TopicAlias { get; set; } + + public byte[] CorrelationData { get; set; } + + public uint? SubscriptionIdentifier { get; set; } } } diff --git a/Source/MQTTnet/MqttApplicationMessageBuilder.cs b/Source/MQTTnet/MqttApplicationMessageBuilder.cs index 79c65ec..1df8645 100644 --- a/Source/MQTTnet/MqttApplicationMessageBuilder.cs +++ b/Source/MQTTnet/MqttApplicationMessageBuilder.cs @@ -3,16 +3,26 @@ using System.IO; using System.Linq; using System.Text; using MQTTnet.Exceptions; +using MQTTnet.Packets; using MQTTnet.Protocol; namespace MQTTnet { public class MqttApplicationMessageBuilder { + private readonly List _userProperties = new List(); + private MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce; private string _topic; private byte[] _payload; private bool _retain; + private string _contentType; + private string _responseTopic; + private byte[] _correlationData; + private ushort? _topicAlias; + private uint? _subscriptionIdentifier; + private uint? _messageExpiryInterval; + private MqttPayloadFormatIndicator? _payloadFormatIndicator; public MqttApplicationMessageBuilder WithTopic(string topic) { @@ -100,6 +110,78 @@ namespace MQTTnet return this; } + /// + /// This is only supported when using MQTTv5. + /// + public MqttApplicationMessageBuilder WithUserProperty(string name, string value) + { + _userProperties.Add(new MqttUserProperty(name, value)); + return this; + } + + /// + /// This is only supported when using MQTTv5. + /// + public MqttApplicationMessageBuilder WithContentType(string contentType) + { + _contentType = contentType; + return this; + } + + /// + /// This is only supported when using MQTTv5. + /// + public MqttApplicationMessageBuilder WithResponseTopic(string responseTopic) + { + _responseTopic = responseTopic; + return this; + } + + /// + /// This is only supported when using MQTTv5. + /// + public MqttApplicationMessageBuilder WithCorrelationData(byte[] correlationData) + { + _correlationData = correlationData; + return this; + } + + /// + /// This is only supported when using MQTTv5. + /// + public MqttApplicationMessageBuilder WithTopicAlias(ushort topicAlias) + { + _topicAlias = topicAlias; + return this; + } + + /// + /// This is only supported when using MQTTv5. + /// + public MqttApplicationMessageBuilder WithSubscriptionIdentifier(uint subscriptionIdentifier) + { + _subscriptionIdentifier = subscriptionIdentifier; + return this; + } + + /// + /// This is only supported when using MQTTv5. + /// + public MqttApplicationMessageBuilder WithMessageExpiryInterval(uint messageExpiryInterval) + { + _messageExpiryInterval = messageExpiryInterval; + return this; + } + + /// + /// This is only supported when using MQTTv5. + /// + public MqttApplicationMessageBuilder WithPayloadFormatIndicator(MqttPayloadFormatIndicator payloadFormatIndicator) + { + _payloadFormatIndicator = payloadFormatIndicator; + return this; + } + public MqttApplicationMessage Build() { if (string.IsNullOrEmpty(_topic)) @@ -107,13 +189,24 @@ namespace MQTTnet throw new MqttProtocolViolationException("Topic is not set."); } - return new MqttApplicationMessage + var applicationMessage = new MqttApplicationMessage { Topic = _topic, Payload = _payload ?? new byte[0], QualityOfServiceLevel = _qualityOfServiceLevel, - Retain = _retain + Retain = _retain, + ContentType = _contentType, + ResponseTopic = _responseTopic, + CorrelationData = _correlationData, + TopicAlias = _topicAlias, + SubscriptionIdentifier = _subscriptionIdentifier, + MessageExpiryInterval = _messageExpiryInterval, + PayloadFormatIndicator = _payloadFormatIndicator }; + + applicationMessage.UserProperties.AddRange(_userProperties); + + return applicationMessage; } } } diff --git a/Source/MQTTnet/Packets/MqttAuthPacket.cs b/Source/MQTTnet/Packets/MqttAuthPacket.cs index 191ec5c..cf93a00 100644 --- a/Source/MQTTnet/Packets/MqttAuthPacket.cs +++ b/Source/MQTTnet/Packets/MqttAuthPacket.cs @@ -8,5 +8,7 @@ namespace MQTTnet.Packets public class MqttAuthPacket : MqttBasePacket { public MqttAuthenticateReasonCode ReasonCode { get; set; } + + public MqttAuthPacketProperties Properties { get; set; } } } diff --git a/Source/MQTTnet/Packets/MqttAuthPacketProperties.cs b/Source/MQTTnet/Packets/MqttAuthPacketProperties.cs new file mode 100644 index 0000000..6ed25ee --- /dev/null +++ b/Source/MQTTnet/Packets/MqttAuthPacketProperties.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace MQTTnet.Packets +{ + public class MqttAuthPacketProperties + { + public string AuthenticationMethod { get; set; } + + public byte[] AuthenticationData { get; set; } + + public string ReasonString { get; set; } + + public List UserProperties { get; } = new List(); + } +} diff --git a/Source/MQTTnet/Packets/MqttConnAckPacket.cs b/Source/MQTTnet/Packets/MqttConnAckPacket.cs index e8caefa..16a492c 100644 --- a/Source/MQTTnet/Packets/MqttConnAckPacket.cs +++ b/Source/MQTTnet/Packets/MqttConnAckPacket.cs @@ -4,14 +4,15 @@ namespace MQTTnet.Packets { public class MqttConnAckPacket : MqttBasePacket { - public MqttConnectReturnCode ConnectReturnCode { get; set; } + public MqttConnectReturnCode? ReturnCode { get; set; } + + #region Added in MQTTv3.1.1 - /// - /// Added in MQTTv3.1.1. - /// public bool IsSessionPresent { get; set; } - #region Added in MQTTv5 + #endregion + + #region Added in MQTTv5.0.0 public MqttConnectReasonCode? ReasonCode { get; set; } @@ -21,7 +22,7 @@ namespace MQTTnet.Packets public override string ToString() { - return string.Concat("ConnAck: [ConnectReturnCode=", ConnectReturnCode, "] [ReasonCode=", ReasonCode, "] [IsSessionPresent=", IsSessionPresent, "]"); + return string.Concat("ConnAck: [ReturnCode=", ReturnCode, "] [ReasonCode=", ReasonCode, "] [IsSessionPresent=", IsSessionPresent, "]"); } } } diff --git a/Source/MQTTnet/Packets/MqttConnAckPacketProperties.cs b/Source/MQTTnet/Packets/MqttConnAckPacketProperties.cs index 31a6b13..262222d 100644 --- a/Source/MQTTnet/Packets/MqttConnAckPacketProperties.cs +++ b/Source/MQTTnet/Packets/MqttConnAckPacketProperties.cs @@ -6,7 +6,6 @@ namespace MQTTnet.Packets { public uint? SessionExpiryInterval { get; set; } - // TODO: Add enum public ushort? ReceiveMaximum { get; set; } public bool? RetainAvailable { get; set; } @@ -19,7 +18,7 @@ namespace MQTTnet.Packets public string ReasonString { get; set; } - public List UserProperties { get; set; } + public List UserProperties { get; } = new List(); public bool? WildcardSubscriptionAvailable { get; set; } diff --git a/Source/MQTTnet/Packets/MqttConnectPacket.cs b/Source/MQTTnet/Packets/MqttConnectPacket.cs index c6b8e6e..8c7ae46 100644 --- a/Source/MQTTnet/Packets/MqttConnectPacket.cs +++ b/Source/MQTTnet/Packets/MqttConnectPacket.cs @@ -14,14 +14,12 @@ public ushort KeepAlivePeriod { get; set; } - /// - /// MQTTv5: Also called "Clean Start". - /// + // Also called "Clean Start" in MQTTv5. public bool CleanSession { get; set; } public MqttApplicationMessage WillMessage { get; set; } - #region Added in MQTTv5 + #region Added in MQTTv5.0.0 public MqttConnectPacketProperties Properties { get; set; } diff --git a/Source/MQTTnet/Packets/MqttConnectPacketProperties.cs b/Source/MQTTnet/Packets/MqttConnectPacketProperties.cs index 4cc405e..25cf5bd 100644 --- a/Source/MQTTnet/Packets/MqttConnectPacketProperties.cs +++ b/Source/MQTTnet/Packets/MqttConnectPacketProperties.cs @@ -22,6 +22,6 @@ namespace MQTTnet.Packets public uint? MaximumPacketSize { get; set; } - public List UserProperties { get; set; } + public List UserProperties { get; } = new List(); } } \ No newline at end of file diff --git a/Source/MQTTnet/Packets/MqttDisconnectPacket.cs b/Source/MQTTnet/Packets/MqttDisconnectPacket.cs index 621829a..c198d9a 100644 --- a/Source/MQTTnet/Packets/MqttDisconnectPacket.cs +++ b/Source/MQTTnet/Packets/MqttDisconnectPacket.cs @@ -1,10 +1,20 @@ -namespace MQTTnet.Packets +using MQTTnet.Protocol; + +namespace MQTTnet.Packets { public class MqttDisconnectPacket : MqttBasePacket { + #region Added in MQTTv5 + + public MqttDisconnectReasonCode? ReasonCode { get; set; } + + public MqttDisconnectPacketProperties Properties { get; set; } + + #endregion + public override string ToString() { - return "Disconnect"; + return string.Concat("Disconnect: [ReasonCode=", ReasonCode, "]"); } } } diff --git a/Source/MQTTnet/Packets/MqttDisconnectPacketProperties.cs b/Source/MQTTnet/Packets/MqttDisconnectPacketProperties.cs new file mode 100644 index 0000000..a5385cb --- /dev/null +++ b/Source/MQTTnet/Packets/MqttDisconnectPacketProperties.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace MQTTnet.Packets +{ + public class MqttDisconnectPacketProperties + { + public uint? SessionExpiryInterval { get; set; } + + public string ReasonString { get; set; } + + public List UserProperties { get; } = new List(); + + public string ServerReference { get; set; } + } +} \ No newline at end of file diff --git a/Source/MQTTnet/Packets/MqttPubAckPacket.cs b/Source/MQTTnet/Packets/MqttPubAckPacket.cs index 2af1642..c919025 100644 --- a/Source/MQTTnet/Packets/MqttPubAckPacket.cs +++ b/Source/MQTTnet/Packets/MqttPubAckPacket.cs @@ -14,7 +14,7 @@ namespace MQTTnet.Packets public override string ToString() { - return string.Concat("PubAck: [PacketIdentifier=", PacketIdentifier, "] [ConnectReasonCode=", ReasonCode, "]"); + return string.Concat("PubAck: [PacketIdentifier=", PacketIdentifier, "] [ReasonCode=", ReasonCode, "]"); } } } diff --git a/Source/MQTTnet/Packets/MqttPubAckPacketProperties.cs b/Source/MQTTnet/Packets/MqttPubAckPacketProperties.cs index e2debd4..061f72f 100644 --- a/Source/MQTTnet/Packets/MqttPubAckPacketProperties.cs +++ b/Source/MQTTnet/Packets/MqttPubAckPacketProperties.cs @@ -6,6 +6,6 @@ namespace MQTTnet.Packets { public string ReasonString { get; set; } - public List UserProperties { get; set; } + public List UserProperties { get; } = new List(); } } diff --git a/Source/MQTTnet/Packets/MqttPubCompPacketProperties.cs b/Source/MQTTnet/Packets/MqttPubCompPacketProperties.cs index 35e040f..c29ad6a 100644 --- a/Source/MQTTnet/Packets/MqttPubCompPacketProperties.cs +++ b/Source/MQTTnet/Packets/MqttPubCompPacketProperties.cs @@ -6,6 +6,6 @@ namespace MQTTnet.Packets { public string ReasonString { get; set; } - public List UserProperties { get; set; } + public List UserProperties { get; } = new List(); } } diff --git a/Source/MQTTnet/Packets/MqttPubRecPacketProperties.cs b/Source/MQTTnet/Packets/MqttPubRecPacketProperties.cs index 0cd7225..574bc61 100644 --- a/Source/MQTTnet/Packets/MqttPubRecPacketProperties.cs +++ b/Source/MQTTnet/Packets/MqttPubRecPacketProperties.cs @@ -6,6 +6,6 @@ namespace MQTTnet.Packets { public string ReasonString { get; set; } - public List UserProperties { get; set; } + public List UserProperties { get; } = new List(); } } diff --git a/Source/MQTTnet/Packets/MqttPubRelPacketProperties.cs b/Source/MQTTnet/Packets/MqttPubRelPacketProperties.cs index aa9625d..d7a21f6 100644 --- a/Source/MQTTnet/Packets/MqttPubRelPacketProperties.cs +++ b/Source/MQTTnet/Packets/MqttPubRelPacketProperties.cs @@ -6,6 +6,6 @@ namespace MQTTnet.Packets { public string ReasonString { get; set; } - public List UserProperties { get; set; } + public List UserProperties { get; } = new List(); } } diff --git a/Source/MQTTnet/Packets/MqttPublishPacketProperties.cs b/Source/MQTTnet/Packets/MqttPublishPacketProperties.cs index ba7254d..0de4d81 100644 --- a/Source/MQTTnet/Packets/MqttPublishPacketProperties.cs +++ b/Source/MQTTnet/Packets/MqttPublishPacketProperties.cs @@ -1,10 +1,11 @@ using System.Collections.Generic; +using MQTTnet.Protocol; namespace MQTTnet.Packets { public class MqttPublishPacketProperties { - public byte? PayloadFormatIndicator { get; set; } + public MqttPayloadFormatIndicator? PayloadFormatIndicator { get; set; } public uint? MessageExpiryInterval { get; set; } @@ -14,7 +15,7 @@ namespace MQTTnet.Packets public byte[] CorrelationData { get; set; } - public List UserProperties { get; set; } + public List UserProperties { get; } = new List(); public uint? SubscriptionIdentifier { get; set; } diff --git a/Source/MQTTnet/Packets/MqttSubAckPacket.cs b/Source/MQTTnet/Packets/MqttSubAckPacket.cs index 45b0e0c..053e713 100644 --- a/Source/MQTTnet/Packets/MqttSubAckPacket.cs +++ b/Source/MQTTnet/Packets/MqttSubAckPacket.cs @@ -8,11 +8,11 @@ namespace MQTTnet.Packets { public ushort? PacketIdentifier { get; set; } - public List SubscribeReturnCodes { get; } = new List(); + public List ReturnCodes { get; } = new List(); - #region Added in MQTTv5 + #region Added in MQTTv5.0.0 - public List ReasonCodes { get; set; } + public List ReasonCodes { get; } = new List(); public MqttSubAckPacketProperties Properties { get; set; } @@ -20,8 +20,10 @@ namespace MQTTnet.Packets public override string ToString() { - var subscribeReturnCodesText = string.Join(",", SubscribeReturnCodes.Select(f => f.ToString())); - return string.Concat("SubAck: [PacketIdentifier=", PacketIdentifier, "] [SubscribeReturnCodes=", subscribeReturnCodesText, "]"); + var returnCodesText = string.Join(",", ReturnCodes.Select(f => f.ToString())); + var reasonCodesText = string.Join(",", ReasonCodes.Select(f => f.ToString())); + + return string.Concat("SubAck: [PacketIdentifier=", PacketIdentifier, "] [ReturnCodes=", returnCodesText, "] [ReasonCode=", reasonCodesText, "]"); } } } diff --git a/Source/MQTTnet/Packets/MqttSubAckPacketProperties.cs b/Source/MQTTnet/Packets/MqttSubAckPacketProperties.cs index 74d3e7f..b2b3eff 100644 --- a/Source/MQTTnet/Packets/MqttSubAckPacketProperties.cs +++ b/Source/MQTTnet/Packets/MqttSubAckPacketProperties.cs @@ -6,6 +6,6 @@ namespace MQTTnet.Packets { public string ReasonString { get; set; } - public List UserProperties { get; set; } + public List UserProperties { get; } = new List(); } } diff --git a/Source/MQTTnet/Packets/MqttSubscribePacket.cs b/Source/MQTTnet/Packets/MqttSubscribePacket.cs index c185f35..c96c75b 100644 --- a/Source/MQTTnet/Packets/MqttSubscribePacket.cs +++ b/Source/MQTTnet/Packets/MqttSubscribePacket.cs @@ -7,7 +7,7 @@ namespace MQTTnet.Packets { public ushort? PacketIdentifier { get; set; } - public IList TopicFilters { get; set; } = new List(); + public List TopicFilters { get; } = new List(); #region Added in MQTTv5 diff --git a/Source/MQTTnet/Packets/MqttSubscribePacketProperties.cs b/Source/MQTTnet/Packets/MqttSubscribePacketProperties.cs index 34f58f2..5b92c5f 100644 --- a/Source/MQTTnet/Packets/MqttSubscribePacketProperties.cs +++ b/Source/MQTTnet/Packets/MqttSubscribePacketProperties.cs @@ -6,6 +6,6 @@ namespace MQTTnet.Packets { public uint? SubscriptionIdentifier { get; set; } - public List UserProperties { get; set; } + public List UserProperties { get; } = new List(); } } diff --git a/Source/MQTTnet/Packets/MqttUnsubAckPacket.cs b/Source/MQTTnet/Packets/MqttUnsubAckPacket.cs index 8b78b53..0b45df6 100644 --- a/Source/MQTTnet/Packets/MqttUnsubAckPacket.cs +++ b/Source/MQTTnet/Packets/MqttUnsubAckPacket.cs @@ -1,12 +1,26 @@ -namespace MQTTnet.Packets +using System.Collections.Generic; +using System.Linq; +using MQTTnet.Protocol; + +namespace MQTTnet.Packets { public class MqttUnsubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier { public ushort? PacketIdentifier { get; set; } + #region Added in MQTTv5 + + public MqttUnsubAckPacketProperties Properties { get; set; } + + public List ReasonCodes { get; } = new List(); + + #endregion + public override string ToString() { - return string.Concat("UnsubAck: [PacketIdentifier=", PacketIdentifier, "]"); + var reasonCodesText = string.Join(",", ReasonCodes.Select(f => f.ToString())); + + return string.Concat("UnsubAck: [PacketIdentifier=", PacketIdentifier, "] [ReasonCodes=", reasonCodesText, "]"); } } } diff --git a/Source/MQTTnet/Packets/MqttUnsubAckPacketProperties.cs b/Source/MQTTnet/Packets/MqttUnsubAckPacketProperties.cs new file mode 100644 index 0000000..ca444d9 --- /dev/null +++ b/Source/MQTTnet/Packets/MqttUnsubAckPacketProperties.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace MQTTnet.Packets +{ + public class MqttUnsubAckPacketProperties + { + public string ReasonString { get; set; } + + public List UserProperties { get; } = new List(); + } +} diff --git a/Source/MQTTnet/Packets/MqttUnsubscribe.cs b/Source/MQTTnet/Packets/MqttUnsubscribePacket.cs similarity index 70% rename from Source/MQTTnet/Packets/MqttUnsubscribe.cs rename to Source/MQTTnet/Packets/MqttUnsubscribePacket.cs index de56386..bb1d4c2 100644 --- a/Source/MQTTnet/Packets/MqttUnsubscribe.cs +++ b/Source/MQTTnet/Packets/MqttUnsubscribePacket.cs @@ -6,7 +6,13 @@ namespace MQTTnet.Packets { public ushort? PacketIdentifier { get; set; } - public IList TopicFilters { get; set; } = new List(); + public List TopicFilters { get; } = new List(); + + #region Added in MQTTv5 + + public MqttUnsubscribePacketProperties Properties { get; set; } + + #endregion public override string ToString() { diff --git a/Source/MQTTnet/Packets/MqttUnsubscribePacketProperties.cs b/Source/MQTTnet/Packets/MqttUnsubscribePacketProperties.cs new file mode 100644 index 0000000..5fb4d66 --- /dev/null +++ b/Source/MQTTnet/Packets/MqttUnsubscribePacketProperties.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace MQTTnet.Packets +{ + public class MqttUnsubscribePacketProperties + { + public List UserProperties { get; } = new List(); + } +} diff --git a/Source/MQTTnet/Protocol/MqttConnectReasonCode.cs b/Source/MQTTnet/Protocol/MqttConnectReasonCode.cs index 381c6fa..cad4d77 100644 --- a/Source/MQTTnet/Protocol/MqttConnectReasonCode.cs +++ b/Source/MQTTnet/Protocol/MqttConnectReasonCode.cs @@ -9,7 +9,7 @@ ImplementationSpecificError = 131, UnsupportedProtocolVersion = 132, ClientIdentifierNotValid = 133, - BadUserNameorPassword = 134, + BadUserNameOrPassword = 134, NotAuthorized = 135, ServerUnavailable = 136, ServerBusy = 137, diff --git a/Source/MQTTnet/Protocol/MqttControlPacketType.cs b/Source/MQTTnet/Protocol/MqttControlPacketType.cs index adb58b6..12dce63 100644 --- a/Source/MQTTnet/Protocol/MqttControlPacketType.cs +++ b/Source/MQTTnet/Protocol/MqttControlPacketType.cs @@ -15,6 +15,7 @@ UnsubAck = 11, PingReq = 12, PingResp = 13, - Disconnect = 14 + Disconnect = 14, + Auth = 15 } } diff --git a/Source/MQTTnet/Protocol/MqttDisconnectReasonCode.cs b/Source/MQTTnet/Protocol/MqttDisconnectReasonCode.cs new file mode 100644 index 0000000..c1d2e3d --- /dev/null +++ b/Source/MQTTnet/Protocol/MqttDisconnectReasonCode.cs @@ -0,0 +1,35 @@ +namespace MQTTnet.Protocol +{ + public enum MqttDisconnectReasonCode + { + NormalDisconnection = 0, + DisconnectWithWillMessage = 4, + UnspecifiedError = 128, + MalformedPacket = 129, + ProtocolError = 130, + ImplementationSpecificError = 131, + NotAuthorized = 135, + ServerBusy = 137, + ServerShuttingDown = 139, + KeepAliveTimeout = 141, + SessionTakenOver = 142, + TopicFilterInvalid = 143, + TopicNameInvalid = 144, + ReceiveMaximumExceeded = 147, + TopicAliasInvalid = 148, + PacketTooLarge = 149, + MessageRateTooHigh = 150, + QuotaExceeded = 151, + AdministrativeAction = 152, + PayloadFormatInvalid = 153, + RetainNotSupported = 154, + QoSNotSupported = 155, + UseAnotherServer = 156, + ServerMoved = 157, + SharedSubscriptionsNotSupported = 158, + ConnectionRateExceeded = 159, + MaximumConnectTime = 160, + SubscriptionIdentifiersNotSupported = 161, + WildcardSubscriptionsNotSupported = 162 + } +} diff --git a/Source/MQTTnet/Protocol/MqttPayloadFormatIndicator.cs b/Source/MQTTnet/Protocol/MqttPayloadFormatIndicator.cs new file mode 100644 index 0000000..bf776a1 --- /dev/null +++ b/Source/MQTTnet/Protocol/MqttPayloadFormatIndicator.cs @@ -0,0 +1,8 @@ +namespace MQTTnet.Protocol +{ + public enum MqttPayloadFormatIndicator + { + Unspecified = 0, + CharacterData = 1 + } +} diff --git a/Source/MQTTnet/Protocol/MqttPropertyID.cs b/Source/MQTTnet/Protocol/MqttPropertyID.cs index 704d8df..2e93a15 100644 --- a/Source/MQTTnet/Protocol/MqttPropertyID.cs +++ b/Source/MQTTnet/Protocol/MqttPropertyID.cs @@ -1,6 +1,6 @@ namespace MQTTnet.Protocol { - public enum MqttPropertyID + public enum MqttPropertyId { PayloadFormatIndicator = 1, MessageExpiryInterval = 2, @@ -27,7 +27,7 @@ UserProperty = 38, MaximumPacketSize = 39, WildcardSubscriptionAvailable = 40, - SubscriptionIdentifierAvailable = 41, + SubscriptionIdentifiersAvailable = 41, SharedSubscriptionAvailable = 42 } } diff --git a/Source/MQTTnet/Protocol/MqttUnsubscribeReasonCode.cs b/Source/MQTTnet/Protocol/MqttUnsubscribeReasonCode.cs new file mode 100644 index 0000000..af06283 --- /dev/null +++ b/Source/MQTTnet/Protocol/MqttUnsubscribeReasonCode.cs @@ -0,0 +1,13 @@ +namespace MQTTnet.Protocol +{ + public enum MqttUnsubscribeReasonCode + { + Success = 0, + NoSubscriptionExisted = 17, + UnspecifiedError = 128, + ImplementationSpecificError = 131, + NotAuthorized = 135, + TopicFilterInvalid = 143, + PacketIdentifierInUse = 145 + } +} diff --git a/Source/MQTTnet/Server/MqttClientSession.cs b/Source/MQTTnet/Server/MqttClientSession.cs index 860467c..6eacfa6 100644 --- a/Source/MQTTnet/Server/MqttClientSession.cs +++ b/Source/MQTTnet/Server/MqttClientSession.cs @@ -7,7 +7,6 @@ using MQTTnet.Client; using MQTTnet.Diagnostics; using MQTTnet.Exceptions; using MQTTnet.Formatter; -using MQTTnet.Internal; using MQTTnet.Packets; using MQTTnet.Protocol; @@ -89,10 +88,10 @@ namespace MQTTnet.Server { if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters)); - _subscriptionsManager.Subscribe(new MqttSubscribePacket - { - TopicFilters = topicFilters - }); + var packet = new MqttSubscribePacket(); + packet.TopicFilters.AddRange(topicFilters); + + _subscriptionsManager.Subscribe(packet); EnqueueSubscribedRetainedMessages(topicFilters); return Task.FromResult(0); @@ -102,10 +101,10 @@ namespace MQTTnet.Server { if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters)); - _subscriptionsManager.Unsubscribe(new MqttUnsubscribePacket - { - TopicFilters = topicFilters - }); + var packet = new MqttUnsubscribePacket(); + packet.TopicFilters.AddRange(topicFilters); + + _subscriptionsManager.Unsubscribe(packet); return Task.FromResult(0); } @@ -278,7 +277,7 @@ namespace MQTTnet.Server return; } - var publishPacket = _channelAdapter.PacketFormatterAdapter.ConvertApplicationMessageToPublishPacket(applicationMessage); + var publishPacket = _channelAdapter.PacketFormatterAdapter.DataConverter.CreatePublishPacket(applicationMessage); // Set the retain flag to true according to [MQTT-3.3.1-8] and [MQTT-3.3.1-9]. publishPacket.Retain = isRetainedApplicationMessage; @@ -345,7 +344,8 @@ namespace MQTTnet.Server { var responsePacket = new MqttPubCompPacket { - PacketIdentifier = pubRelPacket.PacketIdentifier + PacketIdentifier = pubRelPacket.PacketIdentifier, + ReasonCode = MqttPubCompReasonCode.Success }; adapter.SendPacketAsync(responsePacket, cancellationToken).GetAwaiter().GetResult(); @@ -356,7 +356,8 @@ namespace MQTTnet.Server { var responsePacket = new MqttPubRelPacket { - PacketIdentifier = pubRecPacket.PacketIdentifier + PacketIdentifier = pubRecPacket.PacketIdentifier, + ReasonCode = MqttPubRelReasonCode.Success }; adapter.SendPacketAsync(responsePacket, cancellationToken).GetAwaiter().GetResult(); @@ -454,7 +455,9 @@ namespace MQTTnet.Server private void HandleIncomingPublishPacketWithQoS0(MqttPublishPacket publishPacket) { - _sessionsManager.EnqueueApplicationMessage(this, publishPacket.ToApplicationMessage()); + _sessionsManager.EnqueueApplicationMessage( + this, + _channelAdapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket)); } private void HandleIncomingPublishPacketWithQoS1( @@ -462,11 +465,14 @@ namespace MQTTnet.Server MqttPublishPacket publishPacket, CancellationToken cancellationToken) { - _sessionsManager.EnqueueApplicationMessage(this, publishPacket.ToApplicationMessage()); + _sessionsManager.EnqueueApplicationMessage( + this, + _channelAdapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket)); var response = new MqttPubAckPacket { - PacketIdentifier = publishPacket.PacketIdentifier + PacketIdentifier = publishPacket.PacketIdentifier, + ReasonCode = MqttPubAckReasonCode.Success }; adapter.SendPacketAsync(response, cancellationToken).GetAwaiter().GetResult(); @@ -478,11 +484,12 @@ namespace MQTTnet.Server CancellationToken cancellationToken) { // QoS 2 is implement as method "B" (4.3.3 QoS 2: Exactly once delivery) - _sessionsManager.EnqueueApplicationMessage(this, publishPacket.ToApplicationMessage()); + _sessionsManager.EnqueueApplicationMessage(this, _channelAdapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket)); var response = new MqttPubRecPacket { - PacketIdentifier = publishPacket.PacketIdentifier + PacketIdentifier = publishPacket.PacketIdentifier, + ReasonCode = MqttPubRecReasonCode.Success }; adapter.SendPacketAsync(response, cancellationToken).GetAwaiter().GetResult(); diff --git a/Source/MQTTnet/Server/MqttClientSessionsManager.cs b/Source/MQTTnet/Server/MqttClientSessionsManager.cs index a453915..748594a 100644 --- a/Source/MQTTnet/Server/MqttClientSessionsManager.cs +++ b/Source/MQTTnet/Server/MqttClientSessionsManager.cs @@ -215,6 +215,7 @@ namespace MQTTnet.Server try { + // TODO: Catch cancel exception here if the first packet was not received and log properly. var firstPacket = await clientAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout, cancellationToken).ConfigureAwait(false); if (firstPacket == null) { @@ -234,7 +235,7 @@ namespace MQTTnet.Server await clientAdapter.SendPacketAsync( new MqttConnAckPacket { - ConnectReturnCode = connectReturnCode + ReturnCode = connectReturnCode }, cancellationToken).ConfigureAwait(false); @@ -246,7 +247,8 @@ namespace MQTTnet.Server await clientAdapter.SendPacketAsync( new MqttConnAckPacket { - ConnectReturnCode = connectReturnCode, + ReturnCode = connectReturnCode, + ReasonCode = MqttConnectReasonCode.Success, IsSessionPresent = result.IsExistingSession }, cancellationToken).ConfigureAwait(false); diff --git a/Source/MQTTnet/Server/MqttClientSubscriptionsManager.cs b/Source/MQTTnet/Server/MqttClientSubscriptionsManager.cs index 38fad1f..9f1fff6 100644 --- a/Source/MQTTnet/Server/MqttClientSubscriptionsManager.cs +++ b/Source/MQTTnet/Server/MqttClientSubscriptionsManager.cs @@ -39,11 +39,13 @@ namespace MQTTnet.Server var interceptorContext = InterceptSubscribe(topicFilter); if (!interceptorContext.AcceptSubscription) { - result.ResponsePacket.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.Failure); + result.ResponsePacket.ReturnCodes.Add(MqttSubscribeReturnCode.Failure); + result.ResponsePacket.ReasonCodes.Add(MqttSubscribeReasonCode.UnspecifiedError); } else { - result.ResponsePacket.SubscribeReturnCodes.Add(ConvertToMaximumQoS(topicFilter.QualityOfServiceLevel)); + result.ResponsePacket.ReturnCodes.Add(ConvertToSubscribeReturnCode(topicFilter.QualityOfServiceLevel)); + result.ResponsePacket.ReasonCodes.Add(ConvertToSubscribeReasonCode(topicFilter.QualityOfServiceLevel)); } if (interceptorContext.CloseConnection) @@ -69,20 +71,29 @@ namespace MQTTnet.Server { if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket)); + var unsubAckPacket = new MqttUnsubAckPacket + { + PacketIdentifier = unsubscribePacket.PacketIdentifier + }; + lock (_subscriptions) { foreach (var topicFilter in unsubscribePacket.TopicFilters) { - _subscriptions.Remove(topicFilter); + if (_subscriptions.Remove(topicFilter)) + { + unsubAckPacket.ReasonCodes.Add(MqttUnsubscribeReasonCode.Success); + } + else + { + unsubAckPacket.ReasonCodes.Add(MqttUnsubscribeReasonCode.NoSubscriptionExisted); + } _eventDispatcher.OnClientUnsubscribedTopic(_clientId, topicFilter); } } - return new MqttUnsubAckPacket - { - PacketIdentifier = unsubscribePacket.PacketIdentifier - }; + return unsubAckPacket; } public CheckSubscriptionsResult CheckSubscriptions(string topic, MqttQualityOfServiceLevel qosLevel) @@ -113,7 +124,7 @@ namespace MQTTnet.Server return CreateSubscriptionResult(qosLevel, qosLevels); } - private static MqttSubscribeReturnCode ConvertToMaximumQoS(MqttQualityOfServiceLevel qualityOfServiceLevel) + private static MqttSubscribeReturnCode ConvertToSubscribeReturnCode(MqttQualityOfServiceLevel qualityOfServiceLevel) { switch (qualityOfServiceLevel) { @@ -124,6 +135,17 @@ namespace MQTTnet.Server } } + private static MqttSubscribeReasonCode ConvertToSubscribeReasonCode(MqttQualityOfServiceLevel qualityOfServiceLevel) + { + switch (qualityOfServiceLevel) + { + case MqttQualityOfServiceLevel.AtMostOnce: return MqttSubscribeReasonCode.GrantedQoS0; + case MqttQualityOfServiceLevel.AtLeastOnce: return MqttSubscribeReasonCode.GrantedQoS1; + case MqttQualityOfServiceLevel.ExactlyOnce: return MqttSubscribeReasonCode.GrantedQoS2; + default: return MqttSubscribeReasonCode.UnspecifiedError; + } + } + private MqttSubscriptionInterceptorContext InterceptSubscribe(TopicFilter topicFilter) { var interceptorContext = new MqttSubscriptionInterceptorContext(_clientId, topicFilter); diff --git a/Source/MQTTnet/Server/MqttServer.cs b/Source/MQTTnet/Server/MqttServer.cs index 201216a..15e23e7 100644 --- a/Source/MQTTnet/Server/MqttServer.cs +++ b/Source/MQTTnet/Server/MqttServer.cs @@ -4,8 +4,8 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using MQTTnet.Adapter; +using MQTTnet.Client.Publishing; using MQTTnet.Diagnostics; -using MQTTnet.Internal; namespace MQTTnet.Server { @@ -77,7 +77,7 @@ namespace MQTTnet.Server return _clientSessionsManager.UnsubscribeAsync(clientId, topicFilters); } - public Task PublishAsync(MqttApplicationMessage applicationMessage) + public Task PublishAsync(MqttApplicationMessage applicationMessage) { if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage)); @@ -85,7 +85,7 @@ namespace MQTTnet.Server _clientSessionsManager.EnqueueApplicationMessage(null, applicationMessage); - return Task.FromResult(0); + return Task.FromResult(new MqttClientPublishResult()); } public async Task StartAsync(IMqttServerOptions options) diff --git a/Source/MQTTnet/Server/MqttServerOptions.cs b/Source/MQTTnet/Server/MqttServerOptions.cs index 6c7c4f9..02b2bce 100644 --- a/Source/MQTTnet/Server/MqttServerOptions.cs +++ b/Source/MQTTnet/Server/MqttServerOptions.cs @@ -11,6 +11,7 @@ namespace MQTTnet.Server public bool EnablePersistentSessions { get; set; } public int MaxPendingMessagesPerClient { get; set; } = 250; + public MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; set; } = MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage; public TimeSpan DefaultCommunicationTimeout { get; set; } = TimeSpan.FromSeconds(15); diff --git a/Source/MQTTnet/Server/MqttSubscribeResult.cs b/Source/MQTTnet/Server/MqttSubscribeResult.cs deleted file mode 100644 index 2de4004..0000000 --- a/Source/MQTTnet/Server/MqttSubscribeResult.cs +++ /dev/null @@ -1,11 +0,0 @@ -using MQTTnet.Packets; - -namespace MQTTnet.Server -{ - public class MqttSubscribeResult - { - public MqttSubAckPacket ResponsePacket { get; set; } - - public bool CloseConnection { get; set; } - } -} diff --git a/Source/MQTTnet/TopicFilter.cs b/Source/MQTTnet/TopicFilter.cs index 9461562..3b62e7e 100644 --- a/Source/MQTTnet/TopicFilter.cs +++ b/Source/MQTTnet/TopicFilter.cs @@ -4,38 +4,39 @@ namespace MQTTnet { public class TopicFilter { - public TopicFilter(string topic, MqttQualityOfServiceLevel qualityOfServiceLevel) - { - Topic = topic; - QualityOfServiceLevel = qualityOfServiceLevel; - } - public string Topic { get; set; } public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; } - #region Added in MQTTv5 - /// - /// This is only available when using MQTT version 5. + /// This is only supported when using MQTTv5. /// public bool? NoLocal { get; set; } /// - /// This is only available when using MQTT version 5. + /// This is only supported when using MQTTv5. /// public bool? RetainAsPublished { get; set; } /// - /// This is only available when using MQTT version 5. + /// This is only supported when using MQTTv5. /// public MqttRetainHandling? RetainHandling { get; set; } - #endregion - public override string ToString() { - return Topic + "@" + QualityOfServiceLevel; + return string.Concat( + "TopicFilter: [Topic=", + Topic, + "] [QualityOfServiceLevel=", + QualityOfServiceLevel, + "] [NoLocal=", + NoLocal, + "] [RetainAsPublished=", + RetainAsPublished, + "] [RetainHandling=", + RetainHandling, + "]"); } } } \ No newline at end of file diff --git a/Source/MQTTnet/TopicFilterBuilder.cs b/Source/MQTTnet/TopicFilterBuilder.cs index 38c8fbc..a480263 100644 --- a/Source/MQTTnet/TopicFilterBuilder.cs +++ b/Source/MQTTnet/TopicFilterBuilder.cs @@ -45,7 +45,7 @@ namespace MQTTnet throw new MqttProtocolViolationException("Topic is not set."); } - return new TopicFilter(_topic, _qualityOfServiceLevel); + return new TopicFilter { Topic = _topic, QualityOfServiceLevel = _qualityOfServiceLevel }; } } } diff --git a/Tests/MQTTnet.AspNetCore.Tests/MQTTnet.AspNetCore.Tests.csproj b/Tests/MQTTnet.AspNetCore.Tests/MQTTnet.AspNetCore.Tests.csproj index 998acd2..520c7d6 100644 --- a/Tests/MQTTnet.AspNetCore.Tests/MQTTnet.AspNetCore.Tests.csproj +++ b/Tests/MQTTnet.AspNetCore.Tests/MQTTnet.AspNetCore.Tests.csproj @@ -8,12 +8,13 @@ - - + + + diff --git a/Tests/MQTTnet.AspNetCore.Tests/ReaderExtensionsTest.cs b/Tests/MQTTnet.AspNetCore.Tests/ReaderExtensionsTest.cs index 1db41c7..dedeff3 100644 --- a/Tests/MQTTnet.AspNetCore.Tests/ReaderExtensionsTest.cs +++ b/Tests/MQTTnet.AspNetCore.Tests/ReaderExtensionsTest.cs @@ -1,9 +1,7 @@ #if NETCOREAPP using System.Buffers; using Microsoft.VisualStudio.TestTools.UnitTesting; -using MQTTnet.AspNetCore; using MQTTnet.Formatter; -using MQTTnet.Formatter.V311; using MQTTnet.Packets; namespace MQTTnet.AspNetCore.Tests diff --git a/Tests/MQTTnet.Benchmarks/MessageProcessingBenchmark.cs b/Tests/MQTTnet.Benchmarks/MessageProcessingBenchmark.cs index 5e99201..c821ee0 100644 --- a/Tests/MQTTnet.Benchmarks/MessageProcessingBenchmark.cs +++ b/Tests/MQTTnet.Benchmarks/MessageProcessingBenchmark.cs @@ -1,5 +1,6 @@ using BenchmarkDotNet.Attributes; using MQTTnet.Client; +using MQTTnet.Client.Options; using MQTTnet.Server; namespace MQTTnet.Benchmarks diff --git a/Tests/MQTTnet.Benchmarks/MessageProcessingMqttConnectionContextBenchmark.cs b/Tests/MQTTnet.Benchmarks/MessageProcessingMqttConnectionContextBenchmark.cs index 1e2fbb2..7bd085c 100644 --- a/Tests/MQTTnet.Benchmarks/MessageProcessingMqttConnectionContextBenchmark.cs +++ b/Tests/MQTTnet.Benchmarks/MessageProcessingMqttConnectionContextBenchmark.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Hosting; using MQTTnet.Server; using MQTTnet.Diagnostics; using MQTTnet.AspNetCore.Client; +using MQTTnet.Client.Options; namespace MQTTnet.Benchmarks { diff --git a/Tests/MQTTnet.Benchmarks/MqttTcpChannelBenchmark.cs b/Tests/MQTTnet.Benchmarks/MqttTcpChannelBenchmark.cs index 1f29c97..1878c34 100644 --- a/Tests/MQTTnet.Benchmarks/MqttTcpChannelBenchmark.cs +++ b/Tests/MQTTnet.Benchmarks/MqttTcpChannelBenchmark.cs @@ -6,6 +6,7 @@ using MQTTnet.Implementations; using MQTTnet.Server; using System.Threading; using System.Threading.Tasks; +using MQTTnet.Client.Options; namespace MQTTnet.Benchmarks { diff --git a/Tests/MQTTnet.Benchmarks/SerializerBenchmark.cs b/Tests/MQTTnet.Benchmarks/SerializerBenchmark.cs index c7fdb99..b1797ef 100644 --- a/Tests/MQTTnet.Benchmarks/SerializerBenchmark.cs +++ b/Tests/MQTTnet.Benchmarks/SerializerBenchmark.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using MQTTnet.Adapter; using MQTTnet.Channel; using MQTTnet.Formatter; -using MQTTnet.Formatter.V311; +using MQTTnet.Formatter.V3; namespace MQTTnet.Benchmarks { @@ -56,7 +56,7 @@ namespace MQTTnet.Benchmarks var receivedPacket = new ReceivedMqttPacket( header.Flags, - new MqttPacketBodyReader(_serializedPacket.Array, (ulong)(_serializedPacket.Count - header.RemainingLength), (ulong)_serializedPacket.Array.Length)); + new MqttPacketBodyReader(_serializedPacket.Array, (ulong)(_serializedPacket.Count - header.RemainingLength), (ulong)_serializedPacket.Array.Length), 0); _serializer.Decode(receivedPacket); } diff --git a/Tests/MQTTnet.Core.Tests/AsyncAutoResentEventTests.cs b/Tests/MQTTnet.Core.Tests/AsyncAutoResentEventTests.cs index 188f54f..113f3dd 100644 --- a/Tests/MQTTnet.Core.Tests/AsyncAutoResentEventTests.cs +++ b/Tests/MQTTnet.Core.Tests/AsyncAutoResentEventTests.cs @@ -1,10 +1,10 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using MQTTnet.Internal; -using System; +using System; using System.Threading; using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MQTTnet.Internal; -namespace MQTTnet.Core.Tests +namespace MQTTnet.Tests { [TestClass] // Inspired from the vs-threading tests (https://github.com/Microsoft/vs-threading/blob/master/src/Microsoft.VisualStudio.Threading.Tests/AsyncAutoResetEventTests.cs) diff --git a/Tests/MQTTnet.Core.Tests/AsyncLockTests.cs b/Tests/MQTTnet.Core.Tests/AsyncLockTests.cs index 43f2486..81b0ad0 100644 --- a/Tests/MQTTnet.Core.Tests/AsyncLockTests.cs +++ b/Tests/MQTTnet.Core.Tests/AsyncLockTests.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using MQTTnet.Internal; -namespace MQTTnet.Core.Tests +namespace MQTTnet.Tests { [TestClass] public class AsyncLockTests diff --git a/Tests/MQTTnet.Core.Tests/BlockingQueueTests.cs b/Tests/MQTTnet.Core.Tests/BlockingQueueTests.cs index 80e0d72..879302f 100644 --- a/Tests/MQTTnet.Core.Tests/BlockingQueueTests.cs +++ b/Tests/MQTTnet.Core.Tests/BlockingQueueTests.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using MQTTnet.Internal; -namespace MQTTnet.Core.Tests +namespace MQTTnet.Tests { [TestClass] public class BlockingQueueTests diff --git a/Tests/MQTTnet.Core.Tests/ExtensionTests.cs b/Tests/MQTTnet.Core.Tests/ExtensionTests.cs index fdb5888..a2f7562 100644 --- a/Tests/MQTTnet.Core.Tests/ExtensionTests.cs +++ b/Tests/MQTTnet.Core.Tests/ExtensionTests.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using MQTTnet.Exceptions; -namespace MQTTnet.Core.Tests +namespace MQTTnet.Tests { [TestClass] public class ExtensionTests diff --git a/Tests/MQTTnet.Core.Tests/MQTTnet.Core.Tests.csproj b/Tests/MQTTnet.Core.Tests/MQTTnet.Tests.csproj similarity index 86% rename from Tests/MQTTnet.Core.Tests/MQTTnet.Core.Tests.csproj rename to Tests/MQTTnet.Core.Tests/MQTTnet.Tests.csproj index 2baaa28..176cfcd 100644 --- a/Tests/MQTTnet.Core.Tests/MQTTnet.Core.Tests.csproj +++ b/Tests/MQTTnet.Core.Tests/MQTTnet.Tests.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/Tests/MQTTnet.Core.Tests/MQTTv5/MqttClientTests.cs b/Tests/MQTTnet.Core.Tests/MQTTv5/MqttClientTests.cs new file mode 100644 index 0000000..3d7c97c --- /dev/null +++ b/Tests/MQTTnet.Core.Tests/MQTTv5/MqttClientTests.cs @@ -0,0 +1,245 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MQTTnet.Client; +using MQTTnet.Client.Options; +using MQTTnet.Client.Publishing; +using MQTTnet.Client.Subscribing; +using MQTTnet.Client.Unsubscribing; +using MQTTnet.Formatter; +using MQTTnet.Protocol; +using MQTTnet.Server; + +namespace MQTTnet.Tests.MQTTv5 +{ + [TestClass] + public class Client_Tests + { + [TestMethod] + public async Task Client_Connect() + { + var server = new MqttFactory().CreateMqttServer(); + var client = new MqttFactory().CreateMqttClient(); + + try + { + await server.StartAsync(new MqttServerOptions()); + await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").WithProtocolVersion(MqttProtocolVersion.V500).Build()); + } + finally + { + await server.StopAsync(); + } + } + + [TestMethod] + public async Task Client_Connect_And_Disconnect() + { + var server = new MqttFactory().CreateMqttServer(); + var client = new MqttFactory().CreateMqttClient(); + + try + { + await server.StartAsync(new MqttServerOptions()); + + await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").WithProtocolVersion(MqttProtocolVersion.V500).Build()); + await client.DisconnectAsync(); + } + finally + { + await server.StopAsync(); + } + } + + [TestMethod] + public async Task Client_Subscribe() + { + var server = new MqttFactory().CreateMqttServer(); + var client = new MqttFactory().CreateMqttClient(); + + try + { + await server.StartAsync(new MqttServerOptions()); + + await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").WithProtocolVersion(MqttProtocolVersion.V500).Build()); + var result = await client.SubscribeAsync("a", MqttQualityOfServiceLevel.AtLeastOnce); + await client.DisconnectAsync(); + + Assert.AreEqual(1, result.Items.Count); + Assert.AreEqual(MqttClientSubscribeResultCode.GrantedQoS1, result.Items[0].ResultCode); + } + finally + { + await server.StopAsync(); + } + } + + [TestMethod] + public async Task Client_Unsubscribe() + { + var server = new MqttFactory().CreateMqttServer(); + var client = new MqttFactory().CreateMqttClient(); + + try + { + await server.StartAsync(new MqttServerOptions()); + + await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").WithProtocolVersion(MqttProtocolVersion.V500).Build()); + await client.SubscribeAsync("a"); + var result = await client.UnsubscribeAsync("a"); + await client.DisconnectAsync(); + + Assert.AreEqual(1, result.Items.Count); + Assert.AreEqual(MqttClientUnsubscribeResultCode.Success, result.Items[0].ReasonCode); + } + finally + { + await server.StopAsync(); + } + } + + [TestMethod] + public async Task Client_Publish_QoS0() + { + var server = new MqttFactory().CreateMqttServer(); + var client = new MqttFactory().CreateMqttClient(); + + try + { + await server.StartAsync(new MqttServerOptions()); + + await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").WithProtocolVersion(MqttProtocolVersion.V500).Build()); + var result = await client.PublishAsync("a", "b"); + await client.DisconnectAsync(); + + Assert.AreEqual(MqttClientPublishReasonCode.Success, result.ReasonCode); + } + finally + { + await server.StopAsync(); + } + } + + [TestMethod] + public async Task Client_Publish_QoS1() + { + var server = new MqttFactory().CreateMqttServer(); + var client = new MqttFactory().CreateMqttClient(); + + try + { + await server.StartAsync(new MqttServerOptions()); + + await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").WithProtocolVersion(MqttProtocolVersion.V500).Build()); + var result = await client.PublishAsync("a", "b", MqttQualityOfServiceLevel.AtLeastOnce); + await client.DisconnectAsync(); + + Assert.AreEqual(MqttClientPublishReasonCode.Success, result.ReasonCode); + } + finally + { + await server.StopAsync(); + } + } + + [TestMethod] + public async Task Client_Publish_QoS2() + { + var server = new MqttFactory().CreateMqttServer(); + var client = new MqttFactory().CreateMqttClient(); + + try + { + await server.StartAsync(new MqttServerOptions()); + + await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").WithProtocolVersion(MqttProtocolVersion.V500).Build()); + var result = await client.PublishAsync("a", "b", MqttQualityOfServiceLevel.ExactlyOnce); + await client.DisconnectAsync(); + + Assert.AreEqual(MqttClientPublishReasonCode.Success, result.ReasonCode); + } + finally + { + await server.StopAsync(); + } + } + + [TestMethod] + public async Task Client_Publish_With_Properties() + { + var server = new MqttFactory().CreateMqttServer(); + var client = new MqttFactory().CreateMqttClient(); + + try + { + await server.StartAsync(new MqttServerOptions()); + + var applicationMessage = new MqttApplicationMessageBuilder() + .WithTopic("Hello") + .WithPayload("World") + .WithAtMostOnceQoS() + .WithUserProperty("x", "1") + .WithUserProperty("y", "2") + .WithResponseTopic("response") + .WithContentType("text") + .WithMessageExpiryInterval(50) + .WithCorrelationData(new byte[12]) + .WithTopicAlias(2) + .Build(); + + await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").WithProtocolVersion(MqttProtocolVersion.V500).Build()); + var result = await client.PublishAsync(applicationMessage); + await client.DisconnectAsync(); + + Assert.AreEqual(MqttClientPublishReasonCode.Success, result.ReasonCode); + } + finally + { + await server.StopAsync(); + } + } + + [TestMethod] + public async Task Subscribe_And_Publish() + { + var server = new MqttFactory().CreateMqttServer(); + var client1 = new MqttFactory().CreateMqttClient(); + var client2 = new MqttFactory().CreateMqttClient(); + + try + { + await server.StartAsync(new MqttServerOptions()); + + var receivedMessages = new List(); + + await client1.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").WithClientId("client1").WithProtocolVersion(MqttProtocolVersion.V500).Build()); + client1.ApplicationMessageReceived += (s, e) => + { + lock (receivedMessages) + { + receivedMessages.Add(e); + } + }; + + await client1.SubscribeAsync("a"); + + await client2.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").WithClientId("client2").WithProtocolVersion(MqttProtocolVersion.V500).Build()); + await client2.PublishAsync("a", "b"); + + await Task.Delay(500); + + await client2.DisconnectAsync(); + await client1.DisconnectAsync(); + + Assert.AreEqual(1, receivedMessages.Count); + Assert.AreEqual("client1", receivedMessages[0].ClientId); + Assert.AreEqual("a", receivedMessages[0].ApplicationMessage.Topic); + Assert.AreEqual("b", receivedMessages[0].ApplicationMessage.ConvertPayloadToString()); + } + finally + { + await server.StopAsync(); + } + } + } +} diff --git a/Tests/MQTTnet.Core.Tests/ManagedMqttClientTests.cs b/Tests/MQTTnet.Core.Tests/ManagedMqttClientTests.cs index 670b8a1..559a87c 100644 --- a/Tests/MQTTnet.Core.Tests/ManagedMqttClientTests.cs +++ b/Tests/MQTTnet.Core.Tests/ManagedMqttClientTests.cs @@ -1,9 +1,9 @@ -using MQTTnet.Extensions.ManagedClient; -using System.Threading.Tasks; +using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; +using MQTTnet.Extensions.ManagedClient; using MQTTnet.Server; -namespace MQTTnet.Core.Tests +namespace MQTTnet.Tests { [TestClass] public class ManagedMqttClientTests diff --git a/Tests/MQTTnet.Core.Tests/MqttApplicationMessageBuilderTests.cs b/Tests/MQTTnet.Core.Tests/MqttApplicationMessageBuilderTests.cs index 59afcd6..2d99611 100644 --- a/Tests/MQTTnet.Core.Tests/MqttApplicationMessageBuilderTests.cs +++ b/Tests/MQTTnet.Core.Tests/MqttApplicationMessageBuilderTests.cs @@ -4,7 +4,7 @@ using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using MQTTnet.Protocol; -namespace MQTTnet.Core.Tests +namespace MQTTnet.Tests { [TestClass] public class MqttApplicationMessageBuilderTests diff --git a/Tests/MQTTnet.Core.Tests/MqttClientTests.cs b/Tests/MQTTnet.Core.Tests/MqttClientTests.cs index 181a314..6c70caf 100644 --- a/Tests/MQTTnet.Core.Tests/MqttClientTests.cs +++ b/Tests/MQTTnet.Core.Tests/MqttClientTests.cs @@ -2,17 +2,14 @@ using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; -using System.Threading; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; -using MQTTnet.Adapter; using MQTTnet.Client; -using MQTTnet.Diagnostics; +using MQTTnet.Client.Options; using MQTTnet.Exceptions; -using MQTTnet.Packets; using MQTTnet.Server; -namespace MQTTnet.Core.Tests +namespace MQTTnet.Tests { [TestClass] public class MqttClientTests @@ -128,39 +125,39 @@ namespace MQTTnet.Core.Tests } } -#if DEBUG - [TestMethod] - public async Task Client_Cleanup_On_Authentification_Fails() - { - var channel = new TestMqttCommunicationAdapter(); - var channel2 = new TestMqttCommunicationAdapter(); - channel.Partner = channel2; - channel2.Partner = channel; - - Task.Run(async () => { - var connect = await channel2.ReceivePacketAsync(TimeSpan.Zero, CancellationToken.None); - await channel2.SendPacketAsync(new MqttConnAckPacket - { - ConnectReturnCode = Protocol.MqttConnectReturnCode.ConnectionRefusedNotAuthorized - }, CancellationToken.None); - }); +//#if DEBUG +// [TestMethod] +// public async Task Client_Cleanup_On_Authentification_Fails() +// { +// var channel = new TestMqttCommunicationAdapter(); +// var channel2 = new TestMqttCommunicationAdapter(); +// channel.Partner = channel2; +// channel2.Partner = channel; + +// Task.Run(async () => { +// var connect = await channel2.ReceivePacketAsync(TimeSpan.Zero, CancellationToken.None); +// await channel2.SendPacketAsync(new MqttConnAckPacket +// { +// ConnectReturnCode = Protocol.MqttConnectReturnCode.ConnectionRefusedNotAuthorized +// }, CancellationToken.None); +// }); - var fake = new TestMqttCommunicationAdapterFactory(channel); - - var client = new MqttClient(fake, new MqttNetLogger()); - - try - { - await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("any-server").Build()); - } - catch (Exception ex) - { - Assert.IsInstanceOfType(ex, typeof(MqttConnectingFailedException)); - } - - Assert.IsTrue(client._packetReceiverTask == null || client._packetReceiverTask.IsCompleted, "receive loop not completed"); - Assert.IsTrue(client._keepAliveMessageSenderTask == null || client._keepAliveMessageSenderTask.IsCompleted, "keepalive loop not completed"); - } -#endif +// var fake = new TestMqttCommunicationAdapterFactory(channel); + +// var client = new MqttClient(fake, new MqttNetLogger()); + +// try +// { +// await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("any-server").Build()); +// } +// catch (Exception ex) +// { +// Assert.IsInstanceOfType(ex, typeof(MqttConnectingFailedException)); +// } + +// Assert.IsTrue(client._packetReceiverTask == null || client._packetReceiverTask.IsCompleted, "receive loop not completed"); +// Assert.IsTrue(client._keepAliveMessageSenderTask == null || client._keepAliveMessageSenderTask.IsCompleted, "keepalive loop not completed"); +// } +//#endif } } diff --git a/Tests/MQTTnet.Core.Tests/MqttKeepAliveMonitorTests.cs b/Tests/MQTTnet.Core.Tests/MqttKeepAliveMonitorTests.cs index 183955a..91b46de 100644 --- a/Tests/MQTTnet.Core.Tests/MqttKeepAliveMonitorTests.cs +++ b/Tests/MQTTnet.Core.Tests/MqttKeepAliveMonitorTests.cs @@ -8,7 +8,7 @@ using MQTTnet.Diagnostics; using MQTTnet.Packets; using MQTTnet.Server; -namespace MQTTnet.Core.Tests +namespace MQTTnet.Tests { [TestClass] public class MqttKeepAliveMonitorTests diff --git a/Tests/MQTTnet.Core.Tests/MqttPacketIdentifierProviderTests.cs b/Tests/MQTTnet.Core.Tests/MqttPacketIdentifierProviderTests.cs index ab7070e..e5d0cd7 100644 --- a/Tests/MQTTnet.Core.Tests/MqttPacketIdentifierProviderTests.cs +++ b/Tests/MQTTnet.Core.Tests/MqttPacketIdentifierProviderTests.cs @@ -1,7 +1,7 @@ -using Microsoft.VisualStudio.TestTools .UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using MQTTnet.Client; -namespace MQTTnet.Core.Tests +namespace MQTTnet.Tests { [TestClass] public class MqttPacketIdentifierProviderTests diff --git a/Tests/MQTTnet.Core.Tests/MqttPacketReaderTests.cs b/Tests/MQTTnet.Core.Tests/MqttPacketReaderTests.cs index 51c5e07..215dd0a 100644 --- a/Tests/MQTTnet.Core.Tests/MqttPacketReaderTests.cs +++ b/Tests/MQTTnet.Core.Tests/MqttPacketReaderTests.cs @@ -5,7 +5,7 @@ using MQTTnet.Exceptions; using MQTTnet.Formatter; using MQTTnet.Internal; -namespace MQTTnet.Core.Tests +namespace MQTTnet.Tests { [TestClass] public class MqttPacketReaderTests diff --git a/Tests/MQTTnet.Core.Tests/MqttPacketSerializerTests.cs b/Tests/MQTTnet.Core.Tests/MqttPacketSerializerTests.cs index f8bffcd..2b61274 100644 --- a/Tests/MQTTnet.Core.Tests/MqttPacketSerializerTests.cs +++ b/Tests/MQTTnet.Core.Tests/MqttPacketSerializerTests.cs @@ -6,13 +6,12 @@ using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; using MQTTnet.Adapter; using MQTTnet.Formatter; -using MQTTnet.Formatter.V310; -using MQTTnet.Formatter.V311; +using MQTTnet.Formatter.V3; using MQTTnet.Internal; using MQTTnet.Packets; using MQTTnet.Protocol; -namespace MQTTnet.Core.Tests +namespace MQTTnet.Tests { [TestClass] public class MqttPacketSerializerTests @@ -112,7 +111,7 @@ namespace MQTTnet.Core.Tests var p = new MqttConnAckPacket { IsSessionPresent = true, - ConnectReturnCode = MqttConnectReturnCode.ConnectionRefusedNotAuthorized + ReturnCode = MqttConnectReturnCode.ConnectionRefusedNotAuthorized }; SerializeAndCompare(p, "IAIBBQ=="); @@ -123,7 +122,7 @@ namespace MQTTnet.Core.Tests { var p = new MqttConnAckPacket { - ConnectReturnCode = MqttConnectReturnCode.ConnectionRefusedNotAuthorized + ReturnCode = MqttConnectReturnCode.ConnectionRefusedNotAuthorized }; SerializeAndCompare(p, "IAIABQ==", MqttProtocolVersion.V310); @@ -135,7 +134,7 @@ namespace MQTTnet.Core.Tests var p = new MqttConnAckPacket { IsSessionPresent = true, - ConnectReturnCode = MqttConnectReturnCode.ConnectionRefusedNotAuthorized + ReturnCode = MqttConnectReturnCode.ConnectionRefusedNotAuthorized }; DeserializeAndCompare(p, "IAIBBQ=="); @@ -146,7 +145,7 @@ namespace MQTTnet.Core.Tests { var p = new MqttConnAckPacket { - ConnectReturnCode = MqttConnectReturnCode.ConnectionRefusedNotAuthorized + ReturnCode = MqttConnectReturnCode.ConnectionRefusedNotAuthorized }; DeserializeAndCompare(p, "IAIABQ==", MqttProtocolVersion.V310); @@ -189,7 +188,8 @@ namespace MQTTnet.Core.Tests var receivedPacket = new ReceivedMqttPacket( header.Flags, - new MqttPacketBodyReader(buffer.Array, (uint)eof - header.RemainingLength, (uint)(buffer.Count + buffer.Offset))); + new MqttPacketBodyReader(buffer.Array, eof - header.RemainingLength, buffer.Count + buffer.Offset), + 0); var packet = (MqttPublishPacket)serializer.Decode(receivedPacket); @@ -401,9 +401,9 @@ namespace MQTTnet.Core.Tests PacketIdentifier = 123 }; - p.TopicFilters.Add(new TopicFilter("A/B/C", MqttQualityOfServiceLevel.ExactlyOnce)); - p.TopicFilters.Add(new TopicFilter("1/2/3", MqttQualityOfServiceLevel.AtLeastOnce)); - p.TopicFilters.Add(new TopicFilter("x/y/z", MqttQualityOfServiceLevel.AtMostOnce)); + p.TopicFilters.Add(new TopicFilter { Topic = "A/B/C", QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce }); + p.TopicFilters.Add(new TopicFilter { Topic = "1/2/3", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }); + p.TopicFilters.Add(new TopicFilter { Topic = "x/y/z", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }); SerializeAndCompare(p, "ghoAewAFQS9CL0MCAAUxLzIvMwEABXgveS96AA=="); } @@ -416,9 +416,9 @@ namespace MQTTnet.Core.Tests PacketIdentifier = 123 }; - p.TopicFilters.Add(new TopicFilter("A/B/C", MqttQualityOfServiceLevel.ExactlyOnce)); - p.TopicFilters.Add(new TopicFilter("1/2/3", MqttQualityOfServiceLevel.AtLeastOnce)); - p.TopicFilters.Add(new TopicFilter("x/y/z", MqttQualityOfServiceLevel.AtMostOnce)); + p.TopicFilters.Add(new TopicFilter { Topic = "A/B/C", QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce }); + p.TopicFilters.Add(new TopicFilter { Topic = "1/2/3", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }); + p.TopicFilters.Add(new TopicFilter { Topic = "x/y/z", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }); DeserializeAndCompare(p, "ghoAewAFQS9CL0MCAAUxLzIvMwEABXgveS96AA=="); } @@ -431,10 +431,10 @@ namespace MQTTnet.Core.Tests PacketIdentifier = 123 }; - p.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS0); - p.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS1); - p.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS2); - p.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.Failure); + p.ReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS0); + p.ReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS1); + p.ReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS2); + p.ReturnCodes.Add(MqttSubscribeReturnCode.Failure); SerializeAndCompare(p, "kAYAewABAoA="); } @@ -447,10 +447,10 @@ namespace MQTTnet.Core.Tests PacketIdentifier = 123 }; - p.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS0); - p.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS1); - p.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS2); - p.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.Failure); + p.ReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS0); + p.ReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS1); + p.ReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS2); + p.ReturnCodes.Add(MqttSubscribeReturnCode.Failure); DeserializeAndCompare(p, "kAYAewABAoA="); } @@ -552,9 +552,9 @@ namespace MQTTnet.Core.Tests var fixedHeader = new byte[2]; var header = new MqttPacketReader(channel).ReadFixedHeaderAsync(fixedHeader, CancellationToken.None).GetAwaiter().GetResult(); - using (var bodyStream = new MemoryStream(Join(buffer1), (int)headerStream.Position, (int)header.RemainingLength)) + using (var bodyStream = new MemoryStream(Join(buffer1), (int)headerStream.Position, header.RemainingLength)) { - var deserializedPacket = serializer.Decode(new ReceivedMqttPacket(header.Flags, new MqttPacketBodyReader(bodyStream.ToArray(), 0, (int)bodyStream.Length))); + var deserializedPacket = serializer.Decode(new ReceivedMqttPacket(header.Flags, new MqttPacketBodyReader(bodyStream.ToArray(), 0, (int)bodyStream.Length), 0)); var buffer2 = serializer.Encode(deserializedPacket); Assert.AreEqual(expectedBase64Value, Convert.ToBase64String(Join(buffer2))); @@ -590,7 +590,7 @@ namespace MQTTnet.Core.Tests using (var bodyStream = new MemoryStream(Join(buffer1), (int)headerStream.Position, (int)header.RemainingLength)) { - return (T)serializer.Decode(new ReceivedMqttPacket(header.Flags, new MqttPacketBodyReader(bodyStream.ToArray(), 0, (int)bodyStream.Length))); + return (T)serializer.Decode(new ReceivedMqttPacket(header.Flags, new MqttPacketBodyReader(bodyStream.ToArray(), 0, (int)bodyStream.Length), 0)); } } } diff --git a/Tests/MQTTnet.Core.Tests/MqttPacketWriterTests.cs b/Tests/MQTTnet.Core.Tests/MqttPacketWriterTests.cs index a320914..6b4ac8f 100644 --- a/Tests/MQTTnet.Core.Tests/MqttPacketWriterTests.cs +++ b/Tests/MQTTnet.Core.Tests/MqttPacketWriterTests.cs @@ -1,7 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using MQTTnet.Formatter; -namespace MQTTnet.Core.Tests +namespace MQTTnet.Tests { [TestClass] public class MqttPacketWriterTests diff --git a/Tests/MQTTnet.Core.Tests/MqttServerTests.cs b/Tests/MQTTnet.Core.Tests/MqttServerTests.cs index ade6b1b..83f82cd 100644 --- a/Tests/MQTTnet.Core.Tests/MqttServerTests.cs +++ b/Tests/MQTTnet.Core.Tests/MqttServerTests.cs @@ -1,19 +1,19 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using MQTTnet.Client; -using MQTTnet.Diagnostics; -using MQTTnet.Protocol; -using MQTTnet.Server; -using System; -using System.Collections.Concurrent; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; using MQTTnet.Adapter; +using MQTTnet.Client; +using MQTTnet.Client.Options; +using MQTTnet.Diagnostics; using MQTTnet.Implementations; +using MQTTnet.Protocol; +using MQTTnet.Server; -namespace MQTTnet.Core.Tests +namespace MQTTnet.Tests { [TestClass] public class MqttServerTests @@ -111,7 +111,7 @@ namespace MQTTnet.Core.Tests subscribeEventCalled = e.TopicFilter.Topic == "a" && e.ClientId == "c1"; }; - await c1.SubscribeAsync(new TopicFilter("a", MqttQualityOfServiceLevel.AtLeastOnce)); + await c1.SubscribeAsync(new TopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }); await Task.Delay(500); Assert.IsTrue(subscribeEventCalled, "Subscribe event not called."); @@ -158,7 +158,7 @@ namespace MQTTnet.Core.Tests c1.ApplicationMessageReceived += (_, __) => receivedMessagesCount++; var message = new MqttApplicationMessageBuilder().WithTopic("a").WithAtLeastOnceQoS().Build(); - await c1.SubscribeAsync(new TopicFilter("a", MqttQualityOfServiceLevel.AtLeastOnce)); + await c1.SubscribeAsync(new TopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }); await s.PublishAsync(message); await Task.Delay(500); @@ -213,8 +213,8 @@ namespace MQTTnet.Core.Tests }; var message = new MqttApplicationMessageBuilder().WithTopic("a").WithAtLeastOnceQoS().Build(); - await c1.SubscribeAsync(new TopicFilter("a", MqttQualityOfServiceLevel.AtLeastOnce)); - await c2.SubscribeAsync(new TopicFilter("a", MqttQualityOfServiceLevel.AtLeastOnce)); + await c1.SubscribeAsync(new TopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }); + await c2.SubscribeAsync(new TopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }); //await Task.WhenAll(Publish(c1, message), Publish(c2, message)); await Publish(c1, message); @@ -228,7 +228,7 @@ namespace MQTTnet.Core.Tests Assert.AreEqual(2000, receivedMessagesCount); } - + [TestMethod] public async Task MqttServer_SessionTakeover() { @@ -373,7 +373,7 @@ namespace MQTTnet.Core.Tests { await server.StopAsync(); } - + Assert.IsTrue(clientWasConnected); Assert.AreEqual(0, errors); } @@ -554,7 +554,7 @@ namespace MQTTnet.Core.Tests c2.ApplicationMessageReceived += (_, __) => receivedMessagesCount++; await Task.Delay(200); - await c2.SubscribeAsync(new TopicFilter("retained", MqttQualityOfServiceLevel.AtMostOnce)); + await c2.SubscribeAsync(new TopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }); await Task.Delay(500); } finally diff --git a/Tests/MQTTnet.Core.Tests/MqttSubscriptionsManagerTests.cs b/Tests/MQTTnet.Core.Tests/MqttSubscriptionsManagerTests.cs index 2bdd15f..4af3b10 100644 --- a/Tests/MQTTnet.Core.Tests/MqttSubscriptionsManagerTests.cs +++ b/Tests/MQTTnet.Core.Tests/MqttSubscriptionsManagerTests.cs @@ -3,7 +3,7 @@ using MQTTnet.Packets; using MQTTnet.Protocol; using MQTTnet.Server; -namespace MQTTnet.Core.Tests +namespace MQTTnet.Tests { [TestClass] public class MqttSubscriptionsManagerTests @@ -29,7 +29,7 @@ namespace MQTTnet.Core.Tests var sm = new MqttClientSubscriptionsManager("", new MqttServerOptions(), new MqttServerEventDispatcher()); var sp = new MqttSubscribePacket(); - sp.TopicFilters.Add(new TopicFilter("A/B/C", MqttQualityOfServiceLevel.AtMostOnce)); + sp.TopicFilters.Add(new TopicFilter { Topic = "A/B/C", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }); sm.Subscribe(sp); @@ -44,8 +44,8 @@ namespace MQTTnet.Core.Tests var sm = new MqttClientSubscriptionsManager("", new MqttServerOptions(), new MqttServerEventDispatcher()); var sp = new MqttSubscribePacket(); - sp.TopicFilters.Add(new TopicFilter("#", MqttQualityOfServiceLevel.AtMostOnce)); - sp.TopicFilters.Add(new TopicFilter("A/B/C", MqttQualityOfServiceLevel.AtLeastOnce)); + sp.TopicFilters.Add(new TopicFilter { Topic = "#", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }); + sp.TopicFilters.Add(new TopicFilter { Topic = "A/B/C", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }); sm.Subscribe(sp); diff --git a/Tests/MQTTnet.Core.Tests/RoundtripTimeTests.cs b/Tests/MQTTnet.Core.Tests/RoundtripTimeTests.cs index a8e4ce9..c8de9e4 100644 --- a/Tests/MQTTnet.Core.Tests/RoundtripTimeTests.cs +++ b/Tests/MQTTnet.Core.Tests/RoundtripTimeTests.cs @@ -4,9 +4,10 @@ using System.Diagnostics; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using MQTTnet.Client; +using MQTTnet.Client.Options; using MQTTnet.Server; -namespace MQTTnet.Core.Tests +namespace MQTTnet.Tests { [TestClass] public class RoundtripTimeTests diff --git a/Tests/MQTTnet.Core.Tests/TestMqttCommunicationAdapter.cs b/Tests/MQTTnet.Core.Tests/TestMqttCommunicationAdapter.cs index fd95cfa..4a7475d 100644 --- a/Tests/MQTTnet.Core.Tests/TestMqttCommunicationAdapter.cs +++ b/Tests/MQTTnet.Core.Tests/TestMqttCommunicationAdapter.cs @@ -6,7 +6,7 @@ using MQTTnet.Adapter; using MQTTnet.Formatter; using MQTTnet.Packets; -namespace MQTTnet.Core.Tests +namespace MQTTnet.Tests { public class TestMqttCommunicationAdapter : IMqttChannelAdapter { diff --git a/Tests/MQTTnet.Core.Tests/TestMqttCommunicationAdapterFactory.cs b/Tests/MQTTnet.Core.Tests/TestMqttCommunicationAdapterFactory.cs index 6873c69..0263ff6 100644 --- a/Tests/MQTTnet.Core.Tests/TestMqttCommunicationAdapterFactory.cs +++ b/Tests/MQTTnet.Core.Tests/TestMqttCommunicationAdapterFactory.cs @@ -1,8 +1,9 @@ using MQTTnet.Adapter; using MQTTnet.Client; +using MQTTnet.Client.Options; using MQTTnet.Diagnostics; -namespace MQTTnet.Core.Tests +namespace MQTTnet.Tests { public class TestMqttCommunicationAdapterFactory : IMqttClientAdapterFactory { diff --git a/Tests/MQTTnet.Core.Tests/TestMqttServerAdapter.cs b/Tests/MQTTnet.Core.Tests/TestMqttServerAdapter.cs index 2b24f42..cfcba04 100644 --- a/Tests/MQTTnet.Core.Tests/TestMqttServerAdapter.cs +++ b/Tests/MQTTnet.Core.Tests/TestMqttServerAdapter.cs @@ -3,10 +3,11 @@ using System.Threading; using System.Threading.Tasks; using MQTTnet.Adapter; using MQTTnet.Client; +using MQTTnet.Client.Options; using MQTTnet.Diagnostics; using MQTTnet.Server; -namespace MQTTnet.Core.Tests +namespace MQTTnet.Tests { public class TestMqttServerAdapter : IMqttServerAdapter { diff --git a/Tests/MQTTnet.Core.Tests/TestServerExtensions.cs b/Tests/MQTTnet.Core.Tests/TestServerExtensions.cs index 7636b1a..5bfa2c8 100644 --- a/Tests/MQTTnet.Core.Tests/TestServerExtensions.cs +++ b/Tests/MQTTnet.Core.Tests/TestServerExtensions.cs @@ -1,9 +1,8 @@ -using MQTTnet.Client; +using System.Threading.Tasks; +using MQTTnet.Client; using MQTTnet.Server; -using System; -using System.Threading.Tasks; -namespace MQTTnet.Core.Tests +namespace MQTTnet.Tests { public static class TestServerExtensions { diff --git a/Tests/MQTTnet.Core.Tests/TopicFilterComparerTests.cs b/Tests/MQTTnet.Core.Tests/TopicFilterComparerTests.cs index e91e988..244fa3c 100644 --- a/Tests/MQTTnet.Core.Tests/TopicFilterComparerTests.cs +++ b/Tests/MQTTnet.Core.Tests/TopicFilterComparerTests.cs @@ -1,7 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using MQTTnet.Server; -namespace MQTTnet.Core.Tests +namespace MQTTnet.Tests { [TestClass] public class TopicFilterComparerTests diff --git a/Tests/MQTTnet.TestApp.NetCore/ClientFlowTest.cs b/Tests/MQTTnet.TestApp.NetCore/ClientFlowTest.cs index 4b16623..fbeb3bf 100644 --- a/Tests/MQTTnet.TestApp.NetCore/ClientFlowTest.cs +++ b/Tests/MQTTnet.TestApp.NetCore/ClientFlowTest.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using MQTTnet.Client; +using MQTTnet.Client.Options; namespace MQTTnet.TestApp.NetCore { diff --git a/Tests/MQTTnet.TestApp.NetCore/ClientTest.cs b/Tests/MQTTnet.TestApp.NetCore/ClientTest.cs index 98e7f0b..8343d9e 100644 --- a/Tests/MQTTnet.TestApp.NetCore/ClientTest.cs +++ b/Tests/MQTTnet.TestApp.NetCore/ClientTest.cs @@ -2,6 +2,7 @@ using System.Text; using System.Threading.Tasks; using MQTTnet.Client; +using MQTTnet.Client.Options; using MQTTnet.Protocol; namespace MQTTnet.TestApp.NetCore @@ -23,7 +24,7 @@ namespace MQTTnet.TestApp.NetCore Server = "127.0.0.1" } }; - + client.ApplicationMessageReceived += (s, e) => { Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###"); @@ -73,14 +74,14 @@ namespace MQTTnet.TestApp.NetCore { Console.ReadLine(); - await client.SubscribeAsync(new TopicFilter("test", MqttQualityOfServiceLevel.AtMostOnce)); + await client.SubscribeAsync(new TopicFilter { Topic = "test", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }); var applicationMessage = new MqttApplicationMessageBuilder() .WithTopic("A/B/C") .WithPayload("Hello World") .WithAtLeastOnceQoS() .Build(); - + await client.PublishAsync(applicationMessage); } } diff --git a/Tests/MQTTnet.TestApp.NetCore/MQTTnet.TestApp.NetCore.csproj b/Tests/MQTTnet.TestApp.NetCore/MQTTnet.TestApp.NetCore.csproj index 0dd56d7..1900203 100644 --- a/Tests/MQTTnet.TestApp.NetCore/MQTTnet.TestApp.NetCore.csproj +++ b/Tests/MQTTnet.TestApp.NetCore/MQTTnet.TestApp.NetCore.csproj @@ -12,7 +12,7 @@ - + diff --git a/Tests/MQTTnet.TestApp.NetCore/ManagedClientTest.cs b/Tests/MQTTnet.TestApp.NetCore/ManagedClientTest.cs index 563f343..d7cea49 100644 --- a/Tests/MQTTnet.TestApp.NetCore/ManagedClientTest.cs +++ b/Tests/MQTTnet.TestApp.NetCore/ManagedClientTest.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using System.IO; using Newtonsoft.Json; using System.Collections.Generic; -using MQTTnet.Client; +using MQTTnet.Client.Options; using MQTTnet.Extensions.ManagedClient; using MQTTnet.Protocol; @@ -14,7 +14,7 @@ namespace MQTTnet.TestApp.NetCore public static async Task RunAsync() { var ms = new ClientRetainedMessageHandler(); - + var options = new ManagedMqttClientOptions { ClientOptions = new MqttClientOptions @@ -44,9 +44,9 @@ namespace MQTTnet.TestApp.NetCore await managedClient.StartAsync(options); - await managedClient.SubscribeAsync(new TopicFilter("xyz", MqttQualityOfServiceLevel.AtMostOnce)); - await managedClient.SubscribeAsync(new TopicFilter("abc", MqttQualityOfServiceLevel.AtMostOnce)); - + await managedClient.SubscribeAsync(new TopicFilter { Topic = "xyz", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }); + await managedClient.SubscribeAsync(new TopicFilter { Topic = "abc", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }); + await managedClient.PublishAsync(builder => builder.WithTopic("Step").WithPayload("3")); Console.WriteLine("Managed client started."); diff --git a/Tests/MQTTnet.TestApp.NetCore/PerformanceTest.cs b/Tests/MQTTnet.TestApp.NetCore/PerformanceTest.cs index 3f4a1d5..701f45c 100644 --- a/Tests/MQTTnet.TestApp.NetCore/PerformanceTest.cs +++ b/Tests/MQTTnet.TestApp.NetCore/PerformanceTest.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using MQTTnet.Client; +using MQTTnet.Client.Options; using MQTTnet.Protocol; using MQTTnet.Server; diff --git a/Tests/MQTTnet.TestApp.NetCore/Program.cs b/Tests/MQTTnet.TestApp.NetCore/Program.cs index 908844f..e92454e 100644 --- a/Tests/MQTTnet.TestApp.NetCore/Program.cs +++ b/Tests/MQTTnet.TestApp.NetCore/Program.cs @@ -4,6 +4,7 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using MQTTnet.Client; +using MQTTnet.Client.Options; using MQTTnet.Diagnostics; using MQTTnet.Server; using Newtonsoft.Json; diff --git a/Tests/MQTTnet.TestApp.NetCore/PublicBrokerTest.cs b/Tests/MQTTnet.TestApp.NetCore/PublicBrokerTest.cs index e46ed16..ac6d16c 100644 --- a/Tests/MQTTnet.TestApp.NetCore/PublicBrokerTest.cs +++ b/Tests/MQTTnet.TestApp.NetCore/PublicBrokerTest.cs @@ -3,7 +3,8 @@ using System; using System.IO; using System.Threading; using System.Threading.Tasks; -using MQTTnet.Diagnostics; +using MQTTnet.Client.Options; +using MQTTnet.Formatter; using MQTTnet.Protocol; using Newtonsoft.Json; @@ -17,37 +18,37 @@ namespace MQTTnet.TestApp.NetCore // iot.eclipse.org await ExecuteTestAsync("iot.eclipse.org TCP", - new MqttClientOptionsBuilder().WithTcpServer("iot.eclipse.org", 1883).Build()); + new MqttClientOptionsBuilder().WithTcpServer("iot.eclipse.org", 1883).WithProtocolVersion(MqttProtocolVersion.V500).Build()); await ExecuteTestAsync("iot.eclipse.org WS", - new MqttClientOptionsBuilder().WithWebSocketServer("iot.eclipse.org:80/mqtt").Build()); + new MqttClientOptionsBuilder().WithWebSocketServer("iot.eclipse.org:80/mqtt").WithProtocolVersion(MqttProtocolVersion.V500).Build()); await ExecuteTestAsync("iot.eclipse.org WS TLS", - new MqttClientOptionsBuilder().WithWebSocketServer("iot.eclipse.org:443/mqtt").WithTls().Build()); + new MqttClientOptionsBuilder().WithWebSocketServer("iot.eclipse.org:443/mqtt").WithProtocolVersion(MqttProtocolVersion.V500).WithTls().Build()); // test.mosquitto.org await ExecuteTestAsync("test.mosquitto.org TCP", - new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 1883).Build()); + new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 1883).WithProtocolVersion(MqttProtocolVersion.V500).Build()); await ExecuteTestAsync("test.mosquitto.org TCP TLS", - new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883).WithTls().Build()); + new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883).WithProtocolVersion(MqttProtocolVersion.V500).WithTls().Build()); await ExecuteTestAsync("test.mosquitto.org WS", - new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8080/mqtt").Build()); + new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8080/mqtt").WithProtocolVersion(MqttProtocolVersion.V500).Build()); await ExecuteTestAsync("test.mosquitto.org WS TLS", - new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8081/mqtt").WithTls().Build()); + new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8081/mqtt").WithProtocolVersion(MqttProtocolVersion.V500).WithTls().Build()); // broker.hivemq.com await ExecuteTestAsync("broker.hivemq.com TCP", - new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com", 1883).Build()); + new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com", 1883).WithProtocolVersion(MqttProtocolVersion.V500).Build()); await ExecuteTestAsync("broker.hivemq.com WS", - new MqttClientOptionsBuilder().WithWebSocketServer("broker.hivemq.com:8000/mqtt").Build()); + new MqttClientOptionsBuilder().WithWebSocketServer("broker.hivemq.com:8000/mqtt").WithProtocolVersion(MqttProtocolVersion.V500).Build()); // mqtt.swifitch.cz await ExecuteTestAsync("mqtt.swifitch.cz", - new MqttClientOptionsBuilder().WithTcpServer("mqtt.swifitch.cz", 1883).Build()); + new MqttClientOptionsBuilder().WithTcpServer("mqtt.swifitch.cz", 1883).WithProtocolVersion(MqttProtocolVersion.V500).Build()); // CloudMQTT var configFile = Path.Combine("E:\\CloudMqttTestConfig.json"); @@ -56,13 +57,13 @@ namespace MQTTnet.TestApp.NetCore var config = JsonConvert.DeserializeObject(File.ReadAllText(configFile)); await ExecuteTestAsync("CloudMQTT TCP", - new MqttClientOptionsBuilder().WithTcpServer(config.Server, config.Port).WithCredentials(config.Username, config.Password).Build()); + new MqttClientOptionsBuilder().WithTcpServer(config.Server, config.Port).WithCredentials(config.Username, config.Password).WithProtocolVersion(MqttProtocolVersion.V500).Build()); await ExecuteTestAsync("CloudMQTT TCP TLS", - new MqttClientOptionsBuilder().WithTcpServer(config.Server, config.SslPort).WithCredentials(config.Username, config.Password).WithTls().Build()); + new MqttClientOptionsBuilder().WithTcpServer(config.Server, config.SslPort).WithCredentials(config.Username, config.Password).WithTls().WithProtocolVersion(MqttProtocolVersion.V500).Build()); await ExecuteTestAsync("CloudMQTT WS TLS", - new MqttClientOptionsBuilder().WithWebSocketServer(config.Server + ":" + config.SslWebSocketPort + "/mqtt").WithCredentials(config.Username, config.Password).WithTls().Build()); + new MqttClientOptionsBuilder().WithWebSocketServer(config.Server + ":" + config.SslWebSocketPort + "/mqtt").WithCredentials(config.Username, config.Password).WithTls().WithProtocolVersion(MqttProtocolVersion.V500).Build()); } Write("Finished.", ConsoleColor.White); diff --git a/Tests/MQTTnet.TestApp.NetCore/ServerAndClientTest.cs b/Tests/MQTTnet.TestApp.NetCore/ServerAndClientTest.cs index 30ba914..acd01b1 100644 --- a/Tests/MQTTnet.TestApp.NetCore/ServerAndClientTest.cs +++ b/Tests/MQTTnet.TestApp.NetCore/ServerAndClientTest.cs @@ -1,6 +1,7 @@ using System.Threading; using System.Threading.Tasks; using MQTTnet.Client; +using MQTTnet.Client.Options; using MQTTnet.Server; namespace MQTTnet.TestApp.NetCore diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/MQTTnet.TestApp.UniversalWindows.csproj b/Tests/MQTTnet.TestApp.UniversalWindows/MQTTnet.TestApp.UniversalWindows.csproj index 551bd2e..e408f10 100644 --- a/Tests/MQTTnet.TestApp.UniversalWindows/MQTTnet.TestApp.UniversalWindows.csproj +++ b/Tests/MQTTnet.TestApp.UniversalWindows/MQTTnet.TestApp.UniversalWindows.csproj @@ -18,7 +18,7 @@ {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} true MQTTnet.TestApp.UniversalWindows_TemporaryKey.pfx - 05F70CAF1426E296BE2F7396F0B654070D72B930 + 6B99AEE2D6C4A05C2FC9162A71D02388D66DFEEF win10;win10-arm;win10-arm-aot;win10-x86;win10-x86-aot;win10-x64;win10-x64-aot diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/MQTTnet.TestApp.UniversalWindows_TemporaryKey.pfx b/Tests/MQTTnet.TestApp.UniversalWindows/MQTTnet.TestApp.UniversalWindows_TemporaryKey.pfx index 1adc887..0af6b53 100644 Binary files a/Tests/MQTTnet.TestApp.UniversalWindows/MQTTnet.TestApp.UniversalWindows_TemporaryKey.pfx and b/Tests/MQTTnet.TestApp.UniversalWindows/MQTTnet.TestApp.UniversalWindows_TemporaryKey.pfx differ diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml b/Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml index 44f3486..bc1cdf4 100644 --- a/Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml +++ b/Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml @@ -101,26 +101,35 @@ + + + Topic: - Payload: - + Content type: + + + Payload: + - Payload format: - + Payload format: + Plain text Base64 string - QoS level: - + QoS level: + 0 (At most once) 1 (At least once) 2 (Exactly once) + + Response topic: + Retain diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml.cs b/Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml.cs index 6992b3c..44bf102 100644 --- a/Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml.cs +++ b/Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Concurrent; using System.Collections.ObjectModel; -using System.IO; using System.Text; using System.Threading.Tasks; using Windows.Security.Cryptography.Certificates; using Windows.UI.Core; using Windows.UI.Xaml; using MQTTnet.Client; +using MQTTnet.Client.Options; using MQTTnet.Diagnostics; using MQTTnet.Exceptions; using MQTTnet.Extensions.ManagedClient; @@ -16,8 +16,8 @@ using MQTTnet.Formatter; using MQTTnet.Implementations; using MQTTnet.Protocol; using MQTTnet.Server; -using MqttClientConnectedEventArgs = MQTTnet.Client.MqttClientConnectedEventArgs; -using MqttClientDisconnectedEventArgs = MQTTnet.Client.MqttClientDisconnectedEventArgs; +using MqttClientConnectedEventArgs = MQTTnet.Client.Connecting.MqttClientConnectedEventArgs; +using MqttClientDisconnectedEventArgs = MQTTnet.Client.Disconnecting.MqttClientDisconnectedEventArgs; namespace MQTTnet.TestApp.UniversalWindows { @@ -234,6 +234,8 @@ namespace MQTTnet.TestApp.UniversalWindows } var message = new MqttApplicationMessageBuilder() + .WithContentType(ContentType.Text) + .WithResponseTopic(ResponseTopic.Text) .WithTopic(Topic.Text) .WithPayload(payload) .WithQualityOfServiceLevel(qos) @@ -266,7 +268,7 @@ namespace MQTTnet.TestApp.UniversalWindows _mqttClient.Dispose(); _mqttClient = null; } - + if (_managedMqttClient != null) { await _managedMqttClient.StopAsync(); @@ -305,14 +307,16 @@ namespace MQTTnet.TestApp.UniversalWindows qos = MqttQualityOfServiceLevel.ExactlyOnce; } + var topicFilter = new TopicFilter { Topic = SubscribeTopic.Text, QualityOfServiceLevel = qos }; + if (_mqttClient != null) { - await _mqttClient.SubscribeAsync(new TopicFilter(SubscribeTopic.Text, qos)); + await _mqttClient.SubscribeAsync(topicFilter); } if (_managedMqttClient != null) { - await _managedMqttClient.SubscribeAsync(new TopicFilter(SubscribeTopic.Text, qos)); + await _managedMqttClient.SubscribeAsync(topicFilter); } } catch (Exception exception) diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/Package.appxmanifest b/Tests/MQTTnet.TestApp.UniversalWindows/Package.appxmanifest index b677c2a..81914fa 100644 --- a/Tests/MQTTnet.TestApp.UniversalWindows/Package.appxmanifest +++ b/Tests/MQTTnet.TestApp.UniversalWindows/Package.appxmanifest @@ -1,6 +1,6 @@  - + MQTTnet.TestApp.UniversalWindows