Compare commits

...
32 Commits
Author SHA1 Message Date
Christian 38f2453c92 Update README.md 2018-12-22 20:25:53 +01:00
Christian Kratky 058bb65d31 Merge from develop. 2018-12-22 09:40:47 +01:00
Christian Kratky 38b215e9b5 Merge branch 'develop' into feature/MQTTv5 2018-12-22 09:38:12 +01:00
Christian Kratky 45df33c9a5 Update nuspec. 2018-12-22 09:33:44 +01:00
Christian Kratky 89becaa191 Fix build script. 2018-12-22 09:28:51 +01:00
Christian Kratky 6c87e4e039 Replace nuget version with placeholder. 2018-12-22 09:26:52 +01:00
Christian Kratky cbe06d6b87 Merge from master. 2018-12-21 00:21:42 +01:00
Christian Kratky 74e4fd8b3f Update test certificate. 2018-12-20 20:54:37 +01:00
Christian Kratky cc113a6c34 Refactor namespaces. 2018-12-20 20:53:29 +01:00
Christian fca9876111 Delete .travis.yml 2018-12-19 00:10:10 +01:00
Christian ac9f6a36f8 Update appveyor.yml 2018-12-19 00:06:51 +01:00
Christian 929b60e044 Update appveyor.yml 2018-12-18 21:32:46 +01:00
Christian acb433dd4c Update appveyor.yml 2018-12-18 20:23:18 +01:00
Christian c4e8bf7f39 Update appveyor.yml 2018-12-18 20:12:09 +01:00
Christian ba4fdfb51c Merge pull request #497 from cquesnel/feature/add-websocket-parameters
Add custom WebSocket parameters
2018-12-14 21:37:04 +01:00
Charles Quesnel dfda916283 Use default parameter instead of overload 2018-12-13 14:54:31 -05:00
Charles Quesnel 40326df9a9 Fix typo in 'Cookie' 2018-12-13 14:08:53 -05:00
Charles Quesnel a94444098a Remove unused usings 2018-12-13 13:01:34 -05:00
Charles Quesnel 0c13e7a504 Add overload to allow passing custom request headers and cookies when connecting with WS 2018-12-13 12:56:47 -05:00
Christian Kratky 0dba57fe75 Cleanup code. 2018-12-09 21:04:14 +01:00
Christian Kratky 6483f896c1 Merge from develop. 2018-12-09 20:48:46 +01:00
Christian Kratky 8fa9943c3d Merge branch 'master' into develop 2018-12-09 19:44:17 +01:00
Christian Kratky 0f5fb8aa3a Add more pacakges. 2018-11-28 21:11:23 +01:00
Christian Kratky 7baff7c080 Refactor property definition for packets. 2018-11-23 22:50:44 +01:00
Christian Kratky 7292cedd2f Encode ConnAck packet. 2018-11-19 00:17:19 +01:00
Christian Kratky 48d5afa799 Refactor namings of serializers etc. 2018-11-19 00:14:15 +01:00
Christian Kratky 3db22eea51 Remove dedicated V3 messages. 2018-11-18 22:04:39 +01:00
Christian Kratky 2fbb44ec66 Add more property implementations. 2018-11-18 21:54:40 +01:00
Christian Kratky ace25af0e8 Added package properties. 2018-11-16 23:11:26 +01:00
Christian Kratky 8d78455414 Fix breaking changes. 2018-11-16 22:18:59 +01:00
Christian Kratky fed16d7710 Merge from develop. 2018-11-16 22:16:21 +01:00
Christian Kratky 5a59cd278c Add serializer adapter. 2018-08-06 20:36:15 +02:00
192 changed files with 4697 additions and 1011 deletions
-10
View File
@@ -1,10 +0,0 @@
language: csharp
solution: MQTTnet.sln
matrix:
include:
- dotnet: 2.0.0
mono: none
dist: trusty
env: DOTNETCORE=1 # optional, can be used to take different code paths in your script
- mono: latest
+2 -1
View File
@@ -14,7 +14,8 @@
<copyright>Copyright Christian Kratky 2016-2018</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
<dependencies>
<dependency id="MQTTnet" version="2.8.5" />
<dependency id="MQTTnet" version="$nugetVersion" />
<dependency id="Microsoft.AspNetCore.Connections.Abstractions" version="2.1.3" />
<dependency id="Microsoft.AspNetCore.WebSockets" version="2.1.1" />
<dependency id="Microsoft.Extensions.Hosting.Abstractions" version="2.1.1" />
@@ -14,7 +14,7 @@
<copyright>Copyright Christian Kratky 2016-2018</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
<dependencies>
<dependency id="MQTTnet" version="2.8.5" />
<dependency id="MQTTnet" version="$nugetVersion" />
</dependencies>
</metadata>
+1 -1
View File
@@ -14,7 +14,7 @@
<copyright>Copyright Christian Kratky 2016-2018</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
<dependencies>
<dependency id="MQTTnet" version="2.8.5" />
<dependency id="MQTTnet" version="$nugetVersion" />
</dependencies>
</metadata>
+6 -13
View File
@@ -10,19 +10,12 @@
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description>
<releaseNotes> * [Core] Updated nuget packages due to security issues.
* [Client] Fixed wrong behavior of publish method when client is disconnecting (thanks to @PaulFake).
* [Client] Added readonly property for accessing options.
* [ManagedClient] Added max pending messages count option.
* [ManagedClient] Add pending messages overflow strategy option.
* [ManagedClient] Fixed an issue which deletes the wrong message from the internal queue (thanks to @PaulFake).
* [ManagedClient] Added readonly property for accessing options.
* [Server] Added new method which exposes all retained messages.
* [Server] Removed (wrong) setter from the server options interface.
* [Server] Fixed cpu spike in case a client disconnects (issue 421).
* [Server] Fixed concurrent sends with Aspnetcore.Connections.Abstractions based transport.
* [Server] Fixed wrong retain flag when distributing application messages (thanks to @trev0115).
* [Server] Fixed issue which closes a connection when reconnecting with the same client ID (thanks to @fogzot).
<releaseNotes> * [Core] Added support for MQTTv5 packages.
* [Client] Added new MQTTv5 features to options builder.
* [Client] Added uniform API across all supported MQTT versions (BREAKING CHANGE!)
* [Server] Added support for MQTTv5 clients. The server will still return _success_ for all cases at the moment even if more granular codes are available.
* [Note] Due to MQTTv5 a lot of new classes were introduced. This required adding new namespaces as well. Most classes are backward compatible but new namespaces must be added.
</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2018</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
+5 -1
View File
@@ -39,7 +39,11 @@ if ($path) {
Remove-Item .\NuGet -Force -Recurse -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Force -Path .\NuGet
(Get-Content MQTTnet.AspNetCore.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.AspNetCore.nuspec
(Get-Content MQTTnet.Extensions.Rpc.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.Rpc.nuspec
(Get-Content MQTTnet.Extensions.ManagedClient.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.ManagedClient.nuspec
New-Item -ItemType Directory -Force -Path .\NuGet
.\nuget.exe pack MQTTnet.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
.\nuget.exe pack MQTTnet.AspNetCore.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
.\nuget.exe pack MQTTnet.Extensions.Rpc.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

+1 -1
View File
@@ -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
+6 -4
View File
@@ -20,12 +20,13 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
* Async support
* TLS 1.2 support for client and server (but not UWP servers)
* Extensible communication channels (i.e. In-Memory, TCP, TCP+TLS, WS)
* 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 across all supported versions of the 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_.
@@ -48,6 +49,7 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
* WebSockets supported (via ASP.NET Core 2.0, separate nuget)
* A custom message interceptor can be added which allows transforming or extending every received application message
* Validate subscriptions and deny subscribing of certain topics depending on requesting clients
* Connect clients with different protocol versions at the same time.
## Supported frameworks
@@ -62,7 +64,7 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
## Supported MQTT versions
* 5.0.0 (planned)
* 5.0.0
* 3.1.1
* 3.1.0
@@ -95,7 +97,7 @@ If you use this library and want to see your project here please create a pull r
## MIT License
Copyright (c) 2017-2018 Christian Kratky
Copyright (c) 2017-2019 Christian Kratky
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -3,8 +3,9 @@ using System.Net;
using MQTTnet.Adapter;
using MQTTnet.AspNetCore.Client.Tcp;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Diagnostics;
using MQTTnet.Serializer;
using MQTTnet.Formatter;
namespace MQTTnet.AspNetCore.Client
{
@@ -14,15 +15,13 @@ namespace MQTTnet.AspNetCore.Client
{
if (options == null) throw new ArgumentNullException(nameof(options));
var serializer = new MqttPacketSerializer { ProtocolVersion = options.ProtocolVersion };
switch (options.ChannelOptions)
{
case MqttClientTcpOptions tcpOptions:
{
var endpoint = new DnsEndPoint(tcpOptions.Server, tcpOptions.GetPort());
var tcpConnection = new TcpConnection(endpoint);
return new MqttConnectionContext(serializer, tcpConnection);
return new MqttConnectionContext(new MqttPacketFormatterAdapter(options.ProtocolVersion), tcpConnection);
}
default:
{
@@ -3,27 +3,25 @@ using MQTTnet.Adapter;
using MQTTnet.AspNetCore.Client.Tcp;
using MQTTnet.Exceptions;
using MQTTnet.Packets;
using MQTTnet.Serializer;
using System;
using System.IO.Pipelines;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Formatter;
namespace MQTTnet.AspNetCore
{
public class MqttConnectionContext : IMqttChannelAdapter
{
public MqttConnectionContext(
IMqttPacketSerializer packetSerializer,
ConnectionContext connection)
public MqttConnectionContext(MqttPacketFormatterAdapter packetFormatterAdapter, ConnectionContext connection)
{
PacketSerializer = packetSerializer;
Connection = connection;
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
Connection = connection ?? throw new ArgumentNullException(nameof(connection));
}
public string Endpoint => Connection.ConnectionId;
public ConnectionContext Connection { get; }
public IMqttPacketSerializer PacketSerializer { get; }
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
public event EventHandler ReadingPacketStarted;
public event EventHandler ReadingPacketCompleted;
@@ -74,7 +72,7 @@ namespace MQTTnet.AspNetCore
{
if (!buffer.IsEmpty)
{
if (PacketSerializer.TryDeserialize(buffer, out var packet, out consumed, out observed))
if (PacketFormatterAdapter.TryDecode(buffer, out var packet, out consumed, out observed))
{
return packet;
}
@@ -109,7 +107,7 @@ namespace MQTTnet.AspNetCore
public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
{
var buffer = PacketSerializer.Serialize(packet).AsMemory();
var buffer = PacketFormatterAdapter.Encode(packet).AsMemory();
var output = Connection.Transport.Output;
await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
@@ -1,15 +1,15 @@
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Features;
using MQTTnet.Adapter;
using MQTTnet.Serializer;
using MQTTnet.Server;
using System;
using System.Threading.Tasks;
using MQTTnet.Formatter;
namespace MQTTnet.AspNetCore
{
public class MqttConnectionHandler : ConnectionHandler, IMqttServerAdapter
{
{
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
public override async Task OnConnectedAsync(ConnectionContext connection)
@@ -21,9 +21,7 @@ namespace MQTTnet.AspNetCore
transferFormatFeature.ActiveFormat = TransferFormat.Binary;
}
var serializer = new MqttPacketSerializer();
using (var adapter = new MqttConnectionContext(serializer, connection))
using (var adapter = new MqttConnectionContext(new MqttPacketFormatterAdapter(), connection))
{
var args = new MqttServerAdapterClientAcceptedEventArgs(adapter);
ClientAccepted?.Invoke(this, args);
@@ -41,7 +39,7 @@ namespace MQTTnet.AspNetCore
{
return Task.CompletedTask;
}
public void Dispose()
{
}
@@ -3,8 +3,8 @@ using System.Net.WebSockets;
using System.Threading.Tasks;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Formatter;
using MQTTnet.Implementations;
using MQTTnet.Serializer;
using MQTTnet.Server;
namespace MQTTnet.AspNetCore
@@ -27,7 +27,7 @@ namespace MQTTnet.AspNetCore
{
if (webSocket == null) throw new ArgumentNullException(nameof(webSocket));
var clientAdapter = new MqttChannelAdapter(new MqttWebSocketChannel(webSocket, endpoint), new MqttPacketSerializer(), new MqttNetLogger().CreateChildLogger(nameof(MqttWebSocketServerAdapter)));
var clientAdapter = new MqttChannelAdapter(new MqttWebSocketChannel(webSocket, endpoint), new MqttPacketFormatterAdapter(), new MqttNetLogger().CreateChildLogger(nameof(MqttWebSocketServerAdapter)));
var eventArgs = new MqttServerAdapterClientAcceptedEventArgs(clientAdapter);
ClientAccepted?.Invoke(this, eventArgs);
@@ -37,7 +37,7 @@ namespace MQTTnet.AspNetCore
await eventArgs.SessionTask.ConfigureAwait(false);
}
}
public void Dispose()
{
StopAsync().GetAwaiter().GetResult();
@@ -2,19 +2,22 @@
using System.Buffers;
using MQTTnet.Adapter;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Packets;
using MQTTnet.Serializer;
namespace MQTTnet.AspNetCore
{
public static class ReaderExtensions
{
public static bool TryDeserialize(this IMqttPacketSerializer serializer, in ReadOnlySequence<byte> input, out MqttBasePacket packet, out SequencePosition consumed, out SequencePosition observed)
public static bool TryDecode(this MqttPacketFormatterAdapter formatter, in ReadOnlySequence<byte> input, out MqttBasePacket packet, out SequencePosition consumed, out SequencePosition observed)
{
if (formatter == null) throw new ArgumentNullException(nameof(formatter));
packet = null;
consumed = input.Start;
observed = input.End;
var copy = input;
if (copy.Length < 2)
{
return false;
@@ -33,7 +36,7 @@ namespace MQTTnet.AspNetCore
var bodySlice = copy.Slice(0, bodyLength);
var buffer = bodySlice.GetArray();
packet = serializer.Deserialize(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;
@@ -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
{
@@ -1,5 +1,6 @@
using System;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Server;
namespace MQTTnet.Extensions.ManagedClient
@@ -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<MqttClientPublishResult> 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<string>(_unsubscriptions);
_unsubscriptions.Clear();
@@ -1,5 +1,6 @@
using System;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Server;
namespace MQTTnet.Extensions.ManagedClient
@@ -1,5 +1,6 @@
using System;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Server;
namespace MQTTnet.Extensions.ManagedClient
@@ -1,8 +1,8 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Formatter;
using MQTTnet.Packets;
using MQTTnet.Serializer;
namespace MQTTnet.Adapter
{
@@ -10,8 +10,8 @@ namespace MQTTnet.Adapter
{
string Endpoint { get; }
IMqttPacketSerializer PacketSerializer { get; }
MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
event EventHandler ReadingPacketStarted;
event EventHandler ReadingPacketCompleted;
@@ -1,7 +1,7 @@
using MQTTnet.Adapter;
using MQTTnet.Client.Options;
using MQTTnet.Diagnostics;
namespace MQTTnet.Client
namespace MQTTnet.Adapter
{
public interface IMqttClientAdapterFactory
{
+25 -18
View File
@@ -7,9 +7,9 @@ using System.Threading.Tasks;
using MQTTnet.Channel;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Internal;
using MQTTnet.Packets;
using MQTTnet.Serializer;
namespace MQTTnet.Adapter
{
@@ -22,26 +22,27 @@ namespace MQTTnet.Adapter
private readonly IMqttNetChildLogger _logger;
private readonly IMqttChannel _channel;
private readonly MqttPacketReader _packetReader;
private readonly byte[] _fixedHeaderBuffer = new byte[2];
private readonly byte[] _singleByteBuffer = new byte[1];
private bool _isDisposed;
public MqttChannelAdapter(IMqttChannel channel, IMqttPacketSerializer serializer, IMqttNetChildLogger logger)
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, IMqttNetChildLogger logger)
{
if (logger == null) throw new ArgumentNullException(nameof(logger));
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
PacketSerializer = serializer ?? throw new ArgumentNullException(nameof(serializer));
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
_packetReader = new MqttPacketReader(_channel);
_logger = logger.CreateChildLogger(nameof(MqttChannelAdapter));
}
public string Endpoint => _channel.Endpoint;
public IMqttPacketSerializer PacketSerializer { get; }
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
public event EventHandler ReadingPacketStarted;
public event EventHandler ReadingPacketCompleted;
@@ -91,17 +92,17 @@ namespace MQTTnet.Adapter
WrapException(exception);
}
}
public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
{
await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
var packetData = PacketSerializer.Serialize(packet);
var packetData = PacketFormatterAdapter.Encode(packet);
await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false);
PacketSerializer.FreeBuffer();
PacketFormatterAdapter.FreeBuffer();
_logger.Verbose("TX >>> {0}", packet);
_logger.Verbose("TX ({0} bytes) >>> {1}", packetData.Count, packet);
}
catch (Exception exception)
{
@@ -140,13 +141,18 @@ namespace MQTTnet.Adapter
return null;
}
var packet = PacketSerializer.Deserialize(receivedMqttPacket);
if (!PacketFormatterAdapter.ProtocolVersion.HasValue)
{
PacketFormatterAdapter.DetectProtocolVersion(receivedMqttPacket);
}
var packet = PacketFormatterAdapter.Decode(receivedMqttPacket);
if (packet == null)
{
throw new MqttProtocolViolationException("Received malformed packet.");
}
_logger.Verbose("RX <<< {0}", packet);
_logger.Verbose("RX ({0} bytes) <<< {1}", receivedMqttPacket.TotalLength, packet);
return packet;
}
@@ -168,7 +174,7 @@ namespace MQTTnet.Adapter
private async Task<ReceivedMqttPacket> ReceiveAsync(CancellationToken cancellationToken)
{
var fixedHeader = await MqttPacketReader.ReadFixedHeaderAsync(_channel, _fixedHeaderBuffer, _singleByteBuffer, cancellationToken).ConfigureAwait(false);
var fixedHeader = await _packetReader.ReadFixedHeaderAsync(_fixedHeaderBuffer, cancellationToken).ConfigureAwait(false);
try
{
@@ -176,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];
@@ -192,9 +198,9 @@ namespace MQTTnet.Adapter
}
#if WINDOWS_UWP
var readBytes = await _channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken).ConfigureAwait(false);
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, chunkSize, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
#endif
@@ -205,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
{
@@ -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; }
}
}
+5 -2
View File
@@ -1,17 +1,20 @@
using MQTTnet.Serializer;
using MQTTnet.Formatter;
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; }
}
}
@@ -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<MqttClientPublishResult> 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<MqttClientPublishResult> 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<MqttClientPublishResult> 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<MqttClientPublishResult> 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<MqttApplicationMessageBuilder, MqttApplicationMessageBuilder> builder)
public static Task<MqttClientPublishResult> PublishAsync(this IApplicationMessagePublisher publisher, Func<MqttApplicationMessageBuilder, MqttApplicationMessageBuilder> builder)
{
var message = builder(new MqttApplicationMessageBuilder()).Build();
return publisher.PublishAsync(message);
@@ -0,0 +1,9 @@
namespace MQTTnet.Client.Connecting
{
public class MqttClientConnectResult
{
public bool IsSessionPresent { get; set; }
public MqttClientConnectResultCode ResultCode { get; set; }
}
}
@@ -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
}
}
@@ -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; }
}
}
@@ -0,0 +1,9 @@
namespace MQTTnet.Client.Disconnecting
{
public class MqttClientDisconnectOptions
{
public MqttClientDisconnectReason ReasonCode { get; set; } = MqttClientDisconnectReason.NormalDisconnection;
public string ReasonString { get; set; }
}
}
@@ -0,0 +1,8 @@
namespace MQTTnet.Client.Disconnecting
{
public enum MqttClientDisconnectReason
{
NormalDisconnection = 0,
}
}
@@ -1,6 +1,6 @@
using System;
namespace MQTTnet.Client
namespace MQTTnet.Client.Disconnecting
{
public class MqttClientDisconnectedEventArgs : EventArgs
{
+8 -3
View File
@@ -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<MqttClientDisconnectedEventArgs> Disconnected;
Task<MqttClientConnectResult> ConnectAsync(IMqttClientOptions options);
Task DisconnectAsync();
Task DisconnectAsync(MqttClientDisconnectOptions options);
Task<IList<MqttSubscribeResult>> SubscribeAsync(IEnumerable<TopicFilter> topicFilters);
Task UnsubscribeAsync(IEnumerable<string> topics);
Task<MqttClientSubscribeResult> SubscribeAsync(IEnumerable<TopicFilter> topicFilters);
Task<MqttClientUnsubscribeResult> UnsubscribeAsync(IEnumerable<string> topics);
}
}
+2 -1
View File
@@ -1,4 +1,5 @@
using MQTTnet.Diagnostics;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
namespace MQTTnet.Client
{
+103 -54
View File
@@ -5,9 +5,16 @@ using System.Linq;
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.Internal;
using MQTTnet.Formatter;
using MQTTnet.Packets;
using MQTTnet.Protocol;
@@ -69,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();
@@ -80,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)
{
@@ -98,7 +106,7 @@ namespace MQTTnet.Client
}
}
public async Task DisconnectAsync()
public async Task DisconnectAsync(MqttClientDisconnectOptions options)
{
try
{
@@ -106,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
@@ -118,7 +127,7 @@ namespace MQTTnet.Client
}
}
public async Task<IList<MqttSubscribeResult>> SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
public async Task<MqttClientSubscribeResult> SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
{
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
@@ -126,21 +135,16 @@ namespace MQTTnet.Client
var subscribePacket = new MqttSubscribePacket
{
PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier(),
TopicFilters = topicFilters.ToList()
PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier()
};
var response = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket, _cancellationTokenSource.Token).ConfigureAwait(false);
subscribePacket.TopicFilters.AddRange(topicFilters);
if (response.SubscribeReturnCodes.Count != subscribePacket.TopicFilters.Count)
{
throw new MqttProtocolViolationException("The return codes are not matching the topic filters [MQTT-3.9.3-1].");
}
return subscribePacket.TopicFilters.Select((t, i) => new MqttSubscribeResult(t, response.SubscribeReturnCodes[i])).ToList();
var subAckPacket = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket, _cancellationTokenSource.Token).ConfigureAwait(false);
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientSubscribeResult(subscribePacket, subAckPacket);
}
public Task UnsubscribeAsync(IEnumerable<string> topicFilters)
public async Task<MqttClientUnsubscribeResult> UnsubscribeAsync(IEnumerable<string> topicFilters)
{
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
@@ -148,34 +152,47 @@ namespace MQTTnet.Client
var unsubscribePacket = new MqttUnsubscribePacket
{
PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier(),
TopicFilters = topicFilters.ToList()
PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier()
};
return SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket, _cancellationTokenSource.Token);
unsubscribePacket.TopicFilters.AddRange(topicFilters);
var unsubAckPacket = await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket, _cancellationTokenSource.Token).ConfigureAwait(false);
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientUnsubscribeResult(unsubscribePacket, unsubAckPacket);
}
public Task PublishAsync(MqttApplicationMessage applicationMessage)
public async Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage)
{
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
ThrowIfNotConnected();
var publishPacket = applicationMessage.ToPublishPacket();
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<MqttPubAckPacket>(publishPacket, _cancellationTokenSource.Token);
var response = await SendAndReceiveAsync<MqttPubAckPacket>(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:
{
@@ -194,25 +211,22 @@ namespace MQTTnet.Client
_adapter = null;
}
private async Task<MqttConnAckPacket> AuthenticateAsync(MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken)
private async Task<MqttClientConnectResult> 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);
var response = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket, cancellationToken).ConfigureAwait(false);
if (response.ConnectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
var connAckPacket = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket, cancellationToken).ConfigureAwait(false);
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()
@@ -230,7 +244,7 @@ namespace MQTTnet.Client
var clientWasConnected = IsConnected;
InitiateDisconnect();
IsConnected = false;
try
@@ -242,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)
@@ -304,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)
{
@@ -381,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)
{
@@ -436,7 +449,7 @@ namespace MQTTnet.Client
if (packet is MqttDisconnectPacket)
{
return DisconnectAsync();
return DisconnectAsync(null);
}
if (packet is MqttPubRelPacket pubRelPacket)
@@ -459,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.");
@@ -476,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<MqttClientPublishResult> PublishExactlyOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
{
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier();
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
var pubRelPacket = new MqttPubRelPacket
{
PacketIdentifier = pubRecPacket.PacketIdentifier
PacketIdentifier = pubRecPacket.PacketIdentifier,
ReasonCode = MqttPubRelReasonCode.Success
};
await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
var pubCompPacket = await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
var result = new MqttClientPublishResult();
if (pubRecPacket.ReasonCode != null)
{
result.ReasonCode = (MqttClientPublishReasonCode)pubRecPacket.ReasonCode;
}
return result;
}
private void StartReceivingPackets(CancellationToken cancellationToken)
@@ -500,7 +523,7 @@ namespace MQTTnet.Client
_packetReceiverTask = Task.Factory.StartNew(
() => ReceivePacketsAsync(cancellationToken),
cancellationToken,
TaskCreationOptions.LongRunning,
TaskCreationOptions.LongRunning,
TaskScheduler.Default).Unwrap();
}
@@ -517,7 +540,7 @@ namespace MQTTnet.Client
{
try
{
var applicationMessage = publishPacket.ToApplicationMessage();
var applicationMessage = _adapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket);
ApplicationMessageReceived?.Invoke(this, new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage));
}
catch (Exception exception)
@@ -551,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;
}
}
}
@@ -1,12 +0,0 @@
namespace MQTTnet.Client
{
public class MqttClientConnectResult
{
public MqttClientConnectResult(bool isSessionPresent)
{
IsSessionPresent = isSessionPresent;
}
public bool IsSessionPresent { get; }
}
}
@@ -1,14 +0,0 @@
using System;
namespace MQTTnet.Client
{
public class MqttClientConnectedEventArgs : EventArgs
{
public MqttClientConnectedEventArgs(bool isSessionPresent)
{
IsSessionPresent = isSessionPresent;
}
public bool IsSessionPresent { get; }
}
}
+13 -5
View File
@@ -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<IList<MqttSubscribeResult>> 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<MqttClientSubscribeResult> 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<IList<MqttSubscribeResult>> SubscribeAsync(this IMqttClient client, string topic, MqttQualityOfServiceLevel qualityOfServiceLevel)
public static Task<MqttClientSubscribeResult> 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<IList<MqttSubscribeResult>> SubscribeAsync(this IMqttClient client, string topic)
public static Task<MqttClientSubscribeResult> 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<MqttClientUnsubscribeResult> UnsubscribeAsync(this IMqttClient client, params string[] topicFilters)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
@@ -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; }
}
}
@@ -1,4 +1,4 @@
namespace MQTTnet.Client
namespace MQTTnet.Client.Options
{
public interface IMqttClientChannelOptions
{
@@ -1,4 +1,4 @@
namespace MQTTnet.Client
namespace MQTTnet.Client.Options
{
public interface IMqttClientCredentials
{
@@ -1,7 +1,7 @@
using System;
using MQTTnet.Serializer;
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; }
}
}
@@ -1,4 +1,4 @@
namespace MQTTnet.Client
namespace MQTTnet.Client.Options
{
public class MqttClientCredentials : IMqttClientCredentials
{
@@ -1,7 +1,7 @@
using System;
using MQTTnet.Serializer;
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; }
}
}
@@ -1,8 +1,8 @@
using System;
using System.Linq;
using MQTTnet.Serializer;
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
@@ -92,11 +141,13 @@ namespace MQTTnet.Client
return this;
}
public MqttClientOptionsBuilder WithWebSocketServer(string uri)
public MqttClientOptionsBuilder WithWebSocketServer(string uri, MqttClientOptionsBuilderWebSocketParameters parameters = null)
{
_webSocketOptions = new MqttClientWebSocketOptions
{
Uri = uri
Uri = uri,
RequestHeaders = parameters?.RequestHeaders,
CookieContainer = parameters?.CookieContainer
};
return this;
@@ -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
{
@@ -0,0 +1,12 @@
using System.Collections.Generic;
using System.Net;
namespace MQTTnet.Client.Options
{
public class MqttClientOptionsBuilderWebSocketParameters
{
public IDictionary<string, string> RequestHeaders { get; set; }
public CookieContainer CookieContainer { get; set; }
}
}
@@ -1,4 +1,4 @@
namespace MQTTnet.Client
namespace MQTTnet.Client.Options
{
public class MqttClientTcpOptions : IMqttClientChannelOptions
{
@@ -1,6 +1,6 @@
using System;
namespace MQTTnet.Client
namespace MQTTnet.Client.Options
{
public static class MqttClientTcpOptionsExtensions
{
@@ -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
{
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.Net;
namespace MQTTnet.Client
namespace MQTTnet.Client.Options
{
public class MqttClientWebSocketOptions : IMqttClientChannelOptions
{
@@ -1,4 +1,4 @@
namespace MQTTnet.Client
namespace MQTTnet.Client.Options
{
public class MqttClientWebSocketProxyOptions
{
@@ -0,0 +1,12 @@
using System;
using MQTTnet.Packets;
namespace MQTTnet.Client.PacketDispatcher
{
public interface IMqttPacketAwaiter
{
void Complete(MqttBasePacket packet);
void Fail(Exception exception);
}
}
@@ -0,0 +1,37 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Packets;
namespace MQTTnet.Client.PacketDispatcher
{
public sealed class MqttPacketAwaiter<TPacket> : IMqttPacketAwaiter where TPacket : MqttBasePacket
{
private readonly TaskCompletionSource<MqttBasePacket> _packet = new TaskCompletionSource<MqttBasePacket>();
public async Task<TPacket> 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);
}
}
}
@@ -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<Tuple<ushort, Type>, TaskCompletionSource<MqttBasePacket>> _awaiters = new ConcurrentDictionary<Tuple<ushort, Type>, TaskCompletionSource<MqttBasePacket>>();
private readonly ConcurrentDictionary<Tuple<ushort, Type>, IMqttPacketAwaiter> _awaiters = new ConcurrentDictionary<Tuple<ushort, Type>, 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<ushort, Type>(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<MqttBasePacket> AddPacketAwaiter<TResponsePacket>(ushort? identifier) where TResponsePacket : MqttBasePacket
public MqttPacketAwaiter<TResponsePacket> AddPacketAwaiter<TResponsePacket>(ushort? identifier) where TResponsePacket : MqttBasePacket
{
var tcs = new TaskCompletionSource<MqttBasePacket>();
var awaiter = new MqttPacketAwaiter<TResponsePacket>();
if (!identifier.HasValue)
{
identifier = 0;
}
var key = new Tuple<ushort, Type>(identifier ?? 0, typeof(TResponsePacket));
if (!_awaiters.TryAdd(key, tcs))
var key = new Tuple<ushort, Type>(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<TResponsePacket>(ushort? identifier) where TResponsePacket : MqttBasePacket
@@ -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
}
}
@@ -0,0 +1,8 @@

namespace MQTTnet.Client.Publishing
{
public class MqttClientPublishResult
{
public MqttClientPublishReasonCode ReasonCode { get; set; } = MqttClientPublishReasonCode.Success;
}
}
@@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace MQTTnet.Client.Subscribing
{
public class MqttClientSubscribeResult
{
public List<MqttClientSubscribeResultItem> Items { get; } = new List<MqttClientSubscribeResultItem>();
}
}
@@ -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
}
}
@@ -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; }
}
}
@@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace MQTTnet.Client.Unsubscribing
{
public class MqttClientUnsubscribeResult
{
public List<MqttClientUnsubscribeResultItem> Items { get; } =new List<MqttClientUnsubscribeResultItem>();
}
}
@@ -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
}
}
@@ -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; }
}
}
@@ -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);
}
}
@@ -0,0 +1,17 @@
using System;
using MQTTnet.Adapter;
using MQTTnet.Packets;
namespace MQTTnet.Formatter
{
public interface IMqttPacketFormatter
{
IMqttDataConverter DataConverter { get; }
ArraySegment<byte> Encode(MqttBasePacket mqttPacket);
MqttBasePacket Decode(ReceivedMqttPacket receivedMqttPacket);
void FreeBuffer();
}
}
@@ -1,15 +1,18 @@
namespace MQTTnet.Serializer
namespace MQTTnet.Formatter
{
public struct MqttFixedHeader
{
public MqttFixedHeader(byte flags, int remainingLength)
public MqttFixedHeader(byte flags, int remainingLength, int totalLength)
{
Flags = flags;
RemainingLength = remainingLength;
TotalLength = totalLength;
}
public byte Flags { get; }
public int RemainingLength { get; }
public int TotalLength { get; }
}
}
@@ -0,0 +1,147 @@
using System;
using System.Runtime.CompilerServices;
using System.Text;
using MQTTnet.Exceptions;
namespace MQTTnet.Formatter
{
public class MqttPacketBodyReader
{
private readonly byte[] _buffer;
private readonly ulong _initialOffset;
private readonly ulong _length;
public MqttPacketBodyReader(byte[] buffer, int offset, int length)
: this(buffer, (ulong)offset, (ulong)length)
{
}
public MqttPacketBodyReader(byte[] buffer, ulong offset, ulong length)
{
_buffer = buffer;
_initialOffset = offset;
Offset = offset;
_length = length;
}
public ulong Offset { get; private set; }
public ulong Length => _length - Offset;
public bool EndOfStream => Offset == _length;
public void Seek(ulong position)
{
Offset = _initialOffset + position;
}
public ArraySegment<byte> Read(uint length)
{
ValidateReceiveBuffer(length);
var buffer = new ArraySegment<byte>(_buffer, (int)Offset, (int)length);
Offset += length;
return buffer;
}
public byte ReadByte()
{
ValidateReceiveBuffer(1);
return _buffer[Offset++];
}
public bool ReadBoolean()
{
ValidateReceiveBuffer(1);
var buffer = _buffer[Offset++];
if (buffer == 0)
{
return false;
}
if (buffer == 1)
{
return true;
}
throw new MqttProtocolViolationException("Boolean values can be 0 or 1 only.");
}
public ArraySegment<byte> ReadRemainingData()
{
return new ArraySegment<byte>(_buffer, (int)Offset, (int)(_length - Offset));
}
public ushort ReadTwoByteInteger()
{
ValidateReceiveBuffer(2);
var msb = _buffer[Offset++];
var lsb = _buffer[Offset++];
return (ushort)(msb << 8 | lsb);
}
public uint ReadFourByteInteger()
{
ValidateReceiveBuffer(4);
var byte0 = _buffer[Offset++];
var byte1 = _buffer[Offset++];
var byte2 = _buffer[Offset++];
var byte3 = _buffer[Offset++];
return (uint)(byte0 << 24 | byte1 << 16 | byte2 << 8 | byte3);
}
public ArraySegment<byte> ReadWithLengthPrefix()
{
var length = ReadTwoByteInteger();
ValidateReceiveBuffer(length);
var result = new ArraySegment<byte>(_buffer, (int)Offset, length);
Offset += length;
return result;
}
public uint ReadVariableLengthInteger()
{
var multiplier = 1;
var value = 0U;
byte encodedByte;
do
{
encodedByte = ReadByte();
value += (uint)((encodedByte & 127) * multiplier);
if (multiplier > 2097152)
{
throw new MqttProtocolViolationException("Variable length integer is invalid.");
}
multiplier *= 128;
} while ((encodedByte & 128) != 0);
return value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ValidateReceiveBuffer(uint length)
{
if (_length < Offset + length)
{
throw new ArgumentOutOfRangeException(nameof(_buffer), $"Expected at least {Offset + length} bytes but there are only {_length} bytes");
}
}
public string ReadStringWithLengthPrefix()
{
var buffer = ReadWithLengthPrefix();
return Encoding.UTF8.GetString(buffer.Array, buffer.Offset, buffer.Count);
}
}
}
@@ -0,0 +1,140 @@
using System;
using MQTTnet.Adapter;
using MQTTnet.Exceptions;
using MQTTnet.Formatter.V3;
using MQTTnet.Formatter.V5;
using MQTTnet.Packets;
namespace MQTTnet.Formatter
{
public class MqttPacketFormatterAdapter
{
private IMqttPacketFormatter _formatter;
public MqttPacketFormatterAdapter()
{
}
public MqttPacketFormatterAdapter(MqttProtocolVersion protocolVersion)
{
UseProtocolVersion(protocolVersion);
}
public MqttProtocolVersion? ProtocolVersion { get; private set; }
public IMqttDataConverter DataConverter
{
get
{
ThrowIfFormatterNotSet();
return _formatter.DataConverter;
}
}
public ArraySegment<byte> Encode(MqttBasePacket packet)
{
ThrowIfFormatterNotSet();
return _formatter.Encode(packet);
}
public MqttBasePacket Decode(ReceivedMqttPacket receivedMqttPacket)
{
ThrowIfFormatterNotSet();
return _formatter.Decode(receivedMqttPacket);
}
public void FreeBuffer()
{
_formatter?.FreeBuffer();
}
public void DetectProtocolVersion(ReceivedMqttPacket receivedMqttPacket)
{
var protocolVersion = ParseProtocolVersion(receivedMqttPacket);
// 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);
UseProtocolVersion(protocolVersion);
}
private void UseProtocolVersion(MqttProtocolVersion protocolVersion)
{
ProtocolVersion = protocolVersion;
switch (protocolVersion)
{
case MqttProtocolVersion.V500:
{
_formatter = new MqttV500PacketFormatter();
break;
}
case MqttProtocolVersion.V311:
{
_formatter = new MqttV311PacketFormatter();
break;
}
case MqttProtocolVersion.V310:
{
_formatter = new MqttV310PacketFormatter();
break;
}
default:
{
throw new NotSupportedException();
}
}
}
private MqttProtocolVersion ParseProtocolVersion(ReceivedMqttPacket receivedMqttPacket)
{
if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket));
var protocolName = receivedMqttPacket.Body.ReadStringWithLengthPrefix();
var protocolLevel = receivedMqttPacket.Body.ReadByte();
if (protocolName == "MQTT")
{
if (protocolLevel == 5)
{
return MqttProtocolVersion.V500;
}
if (protocolLevel == 4)
{
return MqttProtocolVersion.V311;
}
throw new MqttProtocolViolationException($"Protocol level '{protocolLevel}' not supported.");
}
if (protocolName == "MQIsdp")
{
if (protocolLevel == 3)
{
return MqttProtocolVersion.V310;
}
throw new MqttProtocolViolationException($"Protocol level '{protocolLevel}' not supported.");
}
throw new MqttProtocolViolationException($"Protocol '{protocolName}' not supported.");
}
private void ThrowIfFormatterNotSet()
{
if (_formatter == null)
{
throw new InvalidOperationException("Protocol version not set or detected.");
}
}
}
}
@@ -1,14 +1,24 @@
using System.Threading;
using System;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Channel;
using MQTTnet.Exceptions;
using MQTTnet.Internal;
namespace MQTTnet.Serializer
namespace MQTTnet.Formatter
{
public static class MqttPacketReader
public class MqttPacketReader
{
public static async Task<MqttFixedHeader> ReadFixedHeaderAsync(IMqttChannel channel, byte[] fixedHeaderBuffer, byte[] singleByteBuffer, CancellationToken cancellationToken)
private readonly byte[] _singleByteBuffer = new byte[1];
private readonly IMqttChannel _channel;
public MqttPacketReader(IMqttChannel channel)
{
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
}
public async Task<MqttFixedHeader> ReadFixedHeaderAsync(byte[] fixedHeaderBuffer, CancellationToken cancellationToken)
{
// The MQTT fixed header contains 1 byte of flags and at least 1 byte for the remaining data length.
// So in all cases at least 2 bytes must be read for a complete MQTT packet.
@@ -19,7 +29,7 @@ namespace MQTTnet.Serializer
while (totalBytesRead < buffer.Length)
{
var bytesRead = await channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false);
var bytesRead = await _channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
ExceptionHelper.ThrowIfGracefulSocketClose(bytesRead);
@@ -30,29 +40,30 @@ namespace MQTTnet.Serializer
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(channel, buffer[1], singleByteBuffer, cancellationToken).ConfigureAwait(false);
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(channel, buffer[1], singleByteBuffer, cancellationToken);
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 static int ReadBodyLength(IMqttChannel channel, byte initialEncodedByte, byte[] singleByteBuffer, CancellationToken cancellationToken)
private int ReadBodyLength(byte initialEncodedByte, CancellationToken cancellationToken)
{
var offset = 0;
var multiplier = 128;
var value = initialEncodedByte & 127;
var value = (initialEncodedByte & 127);
int encodedByte = initialEncodedByte;
while ((encodedByte & 128) != 0)
@@ -65,18 +76,18 @@ namespace MQTTnet.Serializer
cancellationToken.ThrowIfCancellationRequested();
encodedByte = ReadByte(channel, singleByteBuffer, cancellationToken);
encodedByte = ReadByte(cancellationToken);
value += (byte)(encodedByte & 127) * multiplier;
value += (encodedByte & 127) * multiplier;
multiplier *= 128;
}
return value;
}
private static byte ReadByte(IMqttChannel channel, byte[] singleByteBuffer, CancellationToken cancellationToken)
private byte ReadByte(CancellationToken cancellationToken)
{
var readCount = channel.ReadAsync(singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
var readCount = _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
cancellationToken.ThrowIfCancellationRequested();
@@ -85,12 +96,12 @@ namespace MQTTnet.Serializer
ExceptionHelper.ThrowGracefulSocketClose();
}
return singleByteBuffer[0];
return _singleByteBuffer[0];
}
#else
private static async Task<int> ReadBodyLengthAsync(IMqttChannel channel, byte initialEncodedByte, byte[] singleByteBuffer, CancellationToken cancellationToken)
private async Task<int> ReadBodyLengthAsync(byte initialEncodedByte, CancellationToken cancellationToken)
{
var offset = 0;
var multiplier = 128;
@@ -107,18 +118,18 @@ namespace MQTTnet.Serializer
cancellationToken.ThrowIfCancellationRequested();
encodedByte = await ReadByteAsync(channel, singleByteBuffer, cancellationToken).ConfigureAwait(false);
encodedByte = await ReadByteAsync(cancellationToken).ConfigureAwait(false);
value += (byte)(encodedByte & 127) * multiplier;
value += (encodedByte & 127) * multiplier;
multiplier *= 128;
}
return value;
}
private static async Task<byte> ReadByteAsync(IMqttChannel channel, byte[] singleByteBuffer, CancellationToken cancellationToken)
private async Task<byte> ReadByteAsync(CancellationToken cancellationToken)
{
var readCount = await channel.ReadAsync(singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false);
var readCount = await _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
@@ -127,7 +138,7 @@ namespace MQTTnet.Serializer
ExceptionHelper.ThrowGracefulSocketClose();
}
return singleByteBuffer[0];
return _singleByteBuffer[0];
}
#endif
@@ -1,8 +1,9 @@
using System;
using System.Runtime.CompilerServices;
using System.Text;
using MQTTnet.Protocol;
namespace MQTTnet.Serializer
namespace MQTTnet.Formatter
{
/// <summary>
/// This is a custom implementation of a memory stream which provides only MQTTnet relevant features.
@@ -16,7 +17,7 @@ namespace MQTTnet.Serializer
private byte[] _buffer = new byte[128];
private int _position;
private int _offset;
public int Length { get; private set; }
@@ -27,9 +28,9 @@ namespace MQTTnet.Serializer
return (byte)fixedHeader;
}
public static ArraySegment<byte> EncodeRemainingLength(int length)
public static ArraySegment<byte> EncodeVariableByteInteger(uint value)
{
if (length <= 0)
if (value <= 0)
{
return new ArraySegment<byte>(new byte[1], 0, 1);
}
@@ -37,7 +38,7 @@ namespace MQTTnet.Serializer
var buffer = new byte[4];
var bufferOffset = 0;
var x = length;
var x = value;
do
{
var encodedByte = x % 128;
@@ -54,6 +55,11 @@ namespace MQTTnet.Serializer
return new ArraySegment<byte>(buffer, 0, bufferOffset);
}
public void WriteVariableLengthInteger(uint value)
{
Write(EncodeVariableByteInteger(value));
}
public void WriteWithLengthPrefix(string value)
{
WriteWithLengthPrefix(Encoding.UTF8.GetBytes(value ?? string.Empty));
@@ -61,17 +67,19 @@ namespace MQTTnet.Serializer
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);
_buffer[_position] = @byte;
_buffer[_offset] = @byte;
IncreasePostition(1);
}
@@ -79,31 +87,50 @@ namespace MQTTnet.Serializer
{
EnsureAdditionalCapacity(2);
_buffer[_position] = (byte)(value >> 8);
_buffer[_offset] = (byte)(value >> 8);
IncreasePostition(1);
_buffer[_position] = (byte)value;
_buffer[_offset] = (byte)value;
IncreasePostition(1);
}
public void Write(byte[] array, int offset, int count)
public void Write(byte[] buffer, int offset, int count)
{
if (array == null) throw new ArgumentNullException(nameof(array));
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
EnsureAdditionalCapacity(count);
Array.Copy(array, offset, _buffer, _position, count);
Array.Copy(buffer, offset, _buffer, _offset, count);
IncreasePostition(count);
}
public void Write(ArraySegment<byte> buffer)
{
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
Write(buffer.Array, buffer.Offset, buffer.Count);
}
public void Write(MqttPacketWriter propertyWriter)
{
if (propertyWriter == null) throw new ArgumentNullException(nameof(propertyWriter));
if (propertyWriter.Length == 0)
{
return;
}
Write(propertyWriter._buffer, 0, propertyWriter.Length);
}
public void Reset()
{
Length = 5;
}
public void Seek(int offset)
public void Seek(int position)
{
EnsureCapacity(offset);
_position = offset;
EnsureCapacity(position);
_offset = position;
}
public byte[] GetBuffer()
@@ -127,9 +154,10 @@ namespace MQTTnet.Serializer
Array.Resize(ref _buffer, MaxBufferSize);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EnsureAdditionalCapacity(int additionalCapacity)
{
var freeSpace = _buffer.Length - _position;
var freeSpace = _buffer.Length - _offset;
if (freeSpace >= additionalCapacity)
{
return;
@@ -138,6 +166,7 @@ namespace MQTTnet.Serializer
EnsureCapacity(_buffer.Length + additionalCapacity - freeSpace);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EnsureCapacity(int capacity)
{
var newBufferLength = _buffer.Length;
@@ -146,7 +175,7 @@ namespace MQTTnet.Serializer
{
return;
}
while (newBufferLength < capacity)
{
newBufferLength *= 2;
@@ -155,13 +184,14 @@ namespace MQTTnet.Serializer
Array.Resize(ref _buffer, newBufferLength);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void IncreasePostition(int length)
{
_position += length;
_offset += length;
if (_position > Length)
if (_offset > Length)
{
Length = _position;
Length = _offset;
}
}
}
@@ -0,0 +1,9 @@
namespace MQTTnet.Formatter
{
public enum MqttProtocolVersion
{
V310 = 3,
V311 = 4,
V500 = 5
}
}
@@ -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;
}
}
}
@@ -1,21 +1,22 @@
using MQTTnet.Exceptions;
using MQTTnet.Packets;
using MQTTnet.Protocol;
using System;
using System;
using System.Linq;
using MQTTnet.Adapter;
using MQTTnet.Exceptions;
using MQTTnet.Internal;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet.Serializer
namespace MQTTnet.Formatter.V3
{
public class MqttPacketSerializer : IMqttPacketSerializer
public class MqttV310PacketFormatter : IMqttPacketFormatter
{
private const int FixedHeaderSize = 1;
private readonly MqttPacketWriter _packetWriter = new MqttPacketWriter();
public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311;
public IMqttDataConverter DataConverter { get; } = new MqttV310DataConverter();
public ArraySegment<byte> Serialize(MqttBasePacket packet)
public ArraySegment<byte> Encode(MqttBasePacket packet)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
@@ -23,15 +24,15 @@ namespace MQTTnet.Serializer
_packetWriter.Reset();
_packetWriter.Seek(5);
var fixedHeader = SerializePacket(packet, _packetWriter);
var remainingLength = _packetWriter.Length - 5;
var fixedHeader = EncodePacket(packet, _packetWriter);
var remainingLength = (uint)(_packetWriter.Length - 5);
var remainingLengthBuffer = MqttPacketWriter.EncodeRemainingLength(remainingLength);
var remainingLengthBuffer = MqttPacketWriter.EncodeVariableByteInteger(remainingLength);
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);
@@ -40,7 +41,7 @@ namespace MQTTnet.Serializer
return new ArraySegment<byte>(buffer, headerOffset, _packetWriter.Length - headerOffset);
}
public MqttBasePacket Deserialize(ReceivedMqttPacket receivedMqttPacket)
public MqttBasePacket Decode(ReceivedMqttPacket receivedMqttPacket)
{
if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket));
@@ -52,20 +53,20 @@ namespace MQTTnet.Serializer
switch ((MqttControlPacketType)controlPacketType)
{
case MqttControlPacketType.Connect: return DeserializeConnect(receivedMqttPacket.Body);
case MqttControlPacketType.ConnAck: return DeserializeConnAck(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 DeserializeSubAck(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.");
}
@@ -76,85 +77,86 @@ namespace MQTTnet.Serializer
_packetWriter.FreeBuffer();
}
private byte SerializePacket(MqttBasePacket packet, MqttPacketWriter packetWriter)
private byte EncodePacket(MqttBasePacket packet, MqttPacketWriter packetWriter)
{
switch (packet)
{
case MqttConnectPacket connectPacket: return Serialize(connectPacket, packetWriter);
case MqttConnAckPacket connAckPacket: return Serialize(connAckPacket, packetWriter);
case MqttDisconnectPacket _: return SerializeEmptyPacket(MqttControlPacketType.Disconnect);
case MqttPingReqPacket _: return SerializeEmptyPacket(MqttControlPacketType.PingReq);
case MqttPingRespPacket _: return SerializeEmptyPacket(MqttControlPacketType.PingResp);
case MqttPublishPacket publishPacket: return Serialize(publishPacket, packetWriter);
case MqttPubAckPacket pubAckPacket: return Serialize(pubAckPacket, packetWriter);
case MqttPubRecPacket pubRecPacket: return Serialize(pubRecPacket, packetWriter);
case MqttPubRelPacket pubRelPacket: return Serialize(pubRelPacket, packetWriter);
case MqttPubCompPacket pubCompPacket: return Serialize(pubCompPacket, packetWriter);
case MqttSubscribePacket subscribePacket: return Serialize(subscribePacket, packetWriter);
case MqttSubAckPacket subAckPacket: return Serialize(subAckPacket, packetWriter);
case MqttUnsubscribePacket unsubscribePacket: return Serialize(unsubscribePacket, packetWriter);
case MqttUnsubAckPacket unsubAckPacket: return Serialize(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);
return new MqttUnsubAckPacket
{
PacketIdentifier = body.ReadUInt16()
PacketIdentifier = body.ReadTwoByteInteger()
};
}
private static MqttBasePacket DeserializePubComp(MqttPacketBodyReader body)
private static MqttBasePacket DecodePubComp(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
return new MqttPubCompPacket
{
PacketIdentifier = body.ReadUInt16()
PacketIdentifier = body.ReadTwoByteInteger()
};
}
private static MqttBasePacket DeserializePubRel(MqttPacketBodyReader body)
private static MqttBasePacket DecodePubRel(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
return new MqttPubRelPacket
{
PacketIdentifier = body.ReadUInt16()
PacketIdentifier = body.ReadTwoByteInteger()
};
}
private static MqttBasePacket DeserializePubRec(MqttPacketBodyReader body)
private static MqttBasePacket DecodePubRec(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
return new MqttPubRecPacket
{
PacketIdentifier = body.ReadUInt16()
PacketIdentifier = body.ReadTwoByteInteger()
};
}
private static MqttBasePacket DeserializePubAck(MqttPacketBodyReader body)
private static MqttBasePacket DecodePubAck(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
return new MqttPubAckPacket
{
PacketIdentifier = body.ReadUInt16()
PacketIdentifier = body.ReadTwoByteInteger()
};
}
private static MqttBasePacket DeserializeUnsubscribe(MqttPacketBodyReader body)
private static MqttBasePacket DecodeUnsubscribe(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttUnsubscribePacket
{
PacketIdentifier = body.ReadUInt16(),
PacketIdentifier = body.ReadTwoByteInteger(),
};
while (!body.EndOfStream)
@@ -165,26 +167,30 @@ namespace MQTTnet.Serializer
return packet;
}
private static MqttBasePacket DeserializeSubscribe(MqttPacketBodyReader body)
private static MqttBasePacket DecodeSubscribe(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttSubscribePacket
{
PacketIdentifier = body.ReadUInt16()
PacketIdentifier = body.ReadTwoByteInteger()
};
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);
@@ -197,7 +203,7 @@ namespace MQTTnet.Serializer
ushort? packetIdentifier = null;
if (qualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
{
packetIdentifier = receivedMqttPacket.Body.ReadUInt16();
packetIdentifier = receivedMqttPacket.Body.ReadTwoByteInteger();
}
var packet = new MqttPublishPacket
@@ -213,37 +219,15 @@ namespace MQTTnet.Serializer
return packet;
}
private static MqttBasePacket DeserializeConnect(MqttPacketBodyReader body)
private MqttBasePacket DecodeConnectPacket(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var protocolName = body.ReadStringWithLengthPrefix();
MqttProtocolVersion protocolVersion;
if (protocolName == "MQTT")
var packet = new MqttConnectPacket
{
var protocolLevel = body.ReadByte();
if (protocolLevel != 4)
{
throw new MqttProtocolViolationException($"Protocol level ({protocolLevel}) not supported for MQTT 3.1.1.");
}
protocolVersion = MqttProtocolVersion.V311;
}
else if (protocolName == "MQIsdp")
{
var protocolLevel = body.ReadByte();
if (protocolLevel != 3)
{
throw new MqttProtocolViolationException($"Protocol level ({protocolLevel}) not supported for MQTT 3.1.");
}
protocolVersion = MqttProtocolVersion.V310;
}
else
{
throw new MqttProtocolViolationException($"Protocol name ({protocolName}) is not supported.");
}
ProtocolName = body.ReadStringWithLengthPrefix(),
ProtocolLevel = body.ReadByte()
};
var connectFlags = body.ReadByte();
if ((connectFlags & 0x1) > 0)
@@ -251,11 +235,7 @@ namespace MQTTnet.Serializer
throw new MqttProtocolViolationException("The first bit of the Connect Flags must be set to 0.");
}
var packet = new MqttConnectPacket
{
ProtocolVersion = protocolVersion,
CleanSession = (connectFlags & 0x2) > 0
};
packet.CleanSession = (connectFlags & 0x2) > 0;
var willFlag = (connectFlags & 0x4) > 0;
var willQoS = (connectFlags & 0x18) >> 3;
@@ -263,7 +243,7 @@ namespace MQTTnet.Serializer
var passwordFlag = (connectFlags & 0x40) > 0;
var usernameFlag = (connectFlags & 0x80) > 0;
packet.KeepAlivePeriod = body.ReadUInt16();
packet.KeepAlivePeriod = body.ReadTwoByteInteger();
packet.ClientId = body.ReadStringWithLengthPrefix();
if (willFlag)
@@ -291,42 +271,36 @@ namespace MQTTnet.Serializer
return packet;
}
private static MqttBasePacket DeserializeSubAck(MqttPacketBodyReader body)
private static MqttBasePacket DecodeSubAck(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttSubAckPacket
{
PacketIdentifier = body.ReadUInt16()
PacketIdentifier = body.ReadTwoByteInteger()
};
while (!body.EndOfStream)
{
packet.SubscribeReturnCodes.Add((MqttSubscribeReturnCode)body.ReadByte());
packet.ReturnCodes.Add((MqttSubscribeReturnCode)body.ReadByte());
}
return packet;
}
private MqttBasePacket DeserializeConnAck(MqttPacketBodyReader body)
protected virtual MqttBasePacket DecodeConnAckPacket(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttConnAckPacket();
var acknowledgeFlags = body.ReadByte();
if (ProtocolVersion == MqttProtocolVersion.V311)
{
packet.IsSessionPresent = (acknowledgeFlags & 0x1) > 0;
}
packet.ConnectReturnCode = (MqttConnectReturnCode)body.ReadByte();
body.ReadByte(); // Reserved.
packet.ReturnCode = (MqttConnectReturnCode)body.ReadByte();
return packet;
}
private static void ValidateConnectPacket(MqttConnectPacket packet)
protected void ValidateConnectPacket(MqttConnectPacket packet)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
@@ -345,21 +319,12 @@ namespace MQTTnet.Serializer
}
}
private byte Serialize(MqttConnectPacket packet, MqttPacketWriter packetWriter)
protected virtual byte EncodeConnectPacket(MqttConnectPacket packet, MqttPacketWriter packetWriter)
{
ValidateConnectPacket(packet);
// Write variable header
if (ProtocolVersion == MqttProtocolVersion.V311)
{
packetWriter.WriteWithLengthPrefix("MQTT");
packetWriter.Write(4); // 3.1.2.2 Protocol Level 4
}
else
{
packetWriter.WriteWithLengthPrefix("MQIsdp");
packetWriter.Write(3); // Protocol Level 3
}
packetWriter.WriteWithLengthPrefix("MQIsdp");
packetWriter.Write(3); // Protocol Level 3
byte connectFlags = 0x0;
if (packet.CleanSession)
@@ -416,33 +381,15 @@ namespace MQTTnet.Serializer
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Connect);
}
private byte Serialize(MqttConnAckPacket packet, MqttPacketWriter packetWriter)
protected virtual byte EncodeConnAckPacket(MqttConnAckPacket packet, MqttPacketWriter packetWriter)
{
if (ProtocolVersion == MqttProtocolVersion.V310)
{
packetWriter.Write(0);
}
else if (ProtocolVersion == MqttProtocolVersion.V311)
{
byte connectAcknowledgeFlags = 0x0;
if (packet.IsSessionPresent)
{
connectAcknowledgeFlags |= 0x1;
}
packetWriter.Write(connectAcknowledgeFlags);
}
else
{
throw new MqttProtocolViolationException("Protocol version not supported.");
}
packetWriter.Write((byte)packet.ConnectReturnCode);
packetWriter.Write(0); // Reserved.
packetWriter.Write((byte)packet.ReturnCode.Value);
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck);
}
private static byte Serialize(MqttPubRelPacket packet, MqttPacketWriter packetWriter)
private static byte EncodePubRelPacket(MqttPubRelPacket packet, MqttPacketWriter packetWriter)
{
if (!packet.PacketIdentifier.HasValue)
{
@@ -454,7 +401,7 @@ namespace MQTTnet.Serializer
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02);
}
private static byte Serialize(MqttPublishPacket packet, MqttPacketWriter packetWriter)
private static byte EncodePublishPacket(MqttPublishPacket packet, MqttPacketWriter packetWriter)
{
ValidatePublishPacket(packet);
@@ -499,7 +446,7 @@ namespace MQTTnet.Serializer
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Publish, fixedHeader);
}
private static byte Serialize(MqttPubAckPacket packet, MqttPacketWriter packetWriter)
private static byte EncodePubAckPacket(MqttPubAckPacket packet, MqttPacketWriter packetWriter)
{
if (!packet.PacketIdentifier.HasValue)
{
@@ -511,7 +458,7 @@ namespace MQTTnet.Serializer
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck);
}
private static byte Serialize(MqttPubRecPacket packet, MqttPacketWriter packetWriter)
private static byte EncodePubRecPacket(MqttPubRecPacket packet, MqttPacketWriter packetWriter)
{
if (!packet.PacketIdentifier.HasValue)
{
@@ -523,7 +470,7 @@ namespace MQTTnet.Serializer
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec);
}
private static byte Serialize(MqttPubCompPacket packet, MqttPacketWriter packetWriter)
private static byte EncodePubCompPacket(MqttPubCompPacket packet, MqttPacketWriter packetWriter)
{
if (!packet.PacketIdentifier.HasValue)
{
@@ -535,7 +482,7 @@ namespace MQTTnet.Serializer
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp);
}
private static byte Serialize(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].");
@@ -558,7 +505,7 @@ namespace MQTTnet.Serializer
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Subscribe, 0x02);
}
private static byte Serialize(MqttSubAckPacket packet, MqttPacketWriter packetWriter)
private static byte EncodeSubAckPacket(MqttSubAckPacket packet, MqttPacketWriter packetWriter)
{
if (!packet.PacketIdentifier.HasValue)
{
@@ -567,9 +514,9 @@ namespace MQTTnet.Serializer
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);
}
@@ -578,7 +525,7 @@ namespace MQTTnet.Serializer
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.SubAck);
}
private static byte Serialize(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].");
@@ -600,7 +547,7 @@ namespace MQTTnet.Serializer
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02);
}
private static byte Serialize(MqttUnsubAckPacket packet, MqttPacketWriter packetWriter)
private static byte EncodeUnsubAckPacket(MqttUnsubAckPacket packet, MqttPacketWriter packetWriter)
{
if (!packet.PacketIdentifier.HasValue)
{
@@ -611,13 +558,13 @@ namespace MQTTnet.Serializer
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck);
}
private static byte SerializeEmptyPacket(MqttControlPacketType type)
private static byte EncodeEmptyPacket(MqttControlPacketType type)
{
return MqttPacketWriter.BuildFixedHeader(type);
}
// ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local
private static void ThrowIfBodyIsEmpty(MqttPacketBodyReader body)
protected static void ThrowIfBodyIsEmpty(MqttPacketBodyReader body)
{
if (body == null || body.Length == 0)
{
@@ -0,0 +1,99 @@
using MQTTnet.Exceptions;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet.Formatter.V3
{
public class MqttV311PacketFormatter : MqttV310PacketFormatter
{
protected override byte EncodeConnectPacket(MqttConnectPacket packet, MqttPacketWriter packetWriter)
{
ValidateConnectPacket(packet);
packetWriter.WriteWithLengthPrefix("MQTT");
packetWriter.Write(4); // 3.1.2.2 Protocol Level 4
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);
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);
}
protected override byte EncodeConnAckPacket(MqttConnAckPacket packet, MqttPacketWriter packetWriter)
{
byte connectAcknowledgeFlags = 0x0;
if (packet.IsSessionPresent)
{
connectAcknowledgeFlags |= 0x1;
}
packetWriter.Write(connectAcknowledgeFlags);
packetWriter.Write((byte)packet.ReturnCode);
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck);
}
protected override MqttBasePacket DecodeConnAckPacket(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttConnAckPacket();
var acknowledgeFlags = body.ReadByte();
packet.IsSessionPresent = (acknowledgeFlags & 0x1) > 0;
packet.ReturnCode = (MqttConnectReturnCode)body.ReadByte();
return packet;
}
}
}
@@ -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<MqttUserProperty>()
};
}
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;
}
}
}
@@ -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.");
}
}
}
}
@@ -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<byte> 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<byte>(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}.");
}
}
}
}
@@ -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<byte> 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();
}
}
}
@@ -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<MqttUserProperty> 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}'.");
}
}
}
@@ -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<MqttUserProperty> 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);
}
}
}
@@ -1,9 +1,10 @@
using System.Threading.Tasks;
using MQTTnet.Client.Publishing;
namespace MQTTnet
{
public interface IApplicationMessagePublisher
{
Task PublishAsync(MqttApplicationMessage applicationMessage);
Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage);
}
}
@@ -1,8 +1,9 @@
using System;
using MQTTnet.Adapter;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Diagnostics;
using MQTTnet.Serializer;
using MQTTnet.Formatter;
namespace MQTTnet.Implementations
{
@@ -11,19 +12,17 @@ namespace MQTTnet.Implementations
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetChildLogger logger)
{
if (options == null) throw new ArgumentNullException(nameof(options));
var serializer = new MqttPacketSerializer { ProtocolVersion = options.ProtocolVersion };
switch (options.ChannelOptions)
{
case MqttClientTcpOptions _:
{
return new MqttChannelAdapter(new MqttTcpChannel(options), serializer, logger);
return new MqttChannelAdapter(new MqttTcpChannel(options), new MqttPacketFormatterAdapter(options.ProtocolVersion), logger);
}
case MqttClientWebSocketOptions webSocketOptions:
{
return new MqttChannelAdapter(new MqttWebSocketChannel(webSocketOptions), serializer, logger);
return new MqttChannelAdapter(new MqttWebSocketChannel(webSocketOptions), new MqttPacketFormatterAdapter(options.ProtocolVersion), logger);
}
default:
@@ -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
@@ -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
{
@@ -4,7 +4,7 @@ using System.Threading.Tasks;
using Windows.Networking.Sockets;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Serializer;
using MQTTnet.Formatter;
using MQTTnet.Server;
namespace MQTTnet.Implementations
@@ -72,7 +72,7 @@ namespace MQTTnet.Implementations
{
try
{
var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket, _options), new MqttPacketSerializer(), _logger);
var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket, _options), new MqttPacketFormatterAdapter(), _logger);
ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
}
catch (Exception exception)
@@ -9,7 +9,7 @@ using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Serializer;
using MQTTnet.Formatter;
using MQTTnet.Server;
namespace MQTTnet.Implementations
@@ -88,7 +88,7 @@ namespace MQTTnet.Implementations
_socket.LocalEndPoint,
_addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6");
var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(clientSocket, sslStream), new MqttPacketSerializer(), _logger);
var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(clientSocket, sslStream), new MqttPacketFormatterAdapter(), _logger);
ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
}
catch (ObjectDisposedException)
@@ -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);
}
@@ -1,8 +1,8 @@
using System;
namespace MQTTnet.Serializer
namespace MQTTnet.Internal
{
public static class Extensions
public static class ArraySegmentExtensions
{
public static byte[] ToArray(this ArraySegment<byte> source)
{
@@ -1,35 +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
};
}
public static MqttPublishPacket ToPublishPacket(this MqttApplicationMessage applicationMessage)
{
if (applicationMessage == null)
{
return null;
}
return new MqttPublishPacket
{
Topic = applicationMessage.Topic,
Payload = applicationMessage.Payload,
QualityOfServiceLevel = applicationMessage.QualityOfServiceLevel,
Retain = applicationMessage.Retain,
Dup = false
};
}
}
}
+16
View File
@@ -63,4 +63,20 @@
<ItemGroup Condition="'$(TargetFramework)'=='net461'">
</ItemGroup>
<ItemGroup>
<Compile Remove="NewFolder\**" />
<EmbeddedResource Remove="NewFolder\**" />
<None Remove="NewFolder\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.2.2" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Net.Requests">
<HintPath>C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Net.Requests.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
+19 -1
View File
@@ -1,4 +1,6 @@
using MQTTnet.Protocol;
using System.Collections.Generic;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet
{
@@ -11,5 +13,21 @@ namespace MQTTnet
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
public bool Retain { get; set; }
public List<MqttUserProperty> UserProperties { get; set; } = new List<MqttUserProperty>();
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; }
}
}
@@ -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<MqttUserProperty> _userProperties = new List<MqttUserProperty>();
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;
}
/// <summary>
/// This is only supported when using MQTTv5.
/// </summary>
public MqttApplicationMessageBuilder WithUserProperty(string name, string value)
{
_userProperties.Add(new MqttUserProperty(name, value));
return this;
}
/// <summary>
/// This is only supported when using MQTTv5.
/// </summary>
public MqttApplicationMessageBuilder WithContentType(string contentType)
{
_contentType = contentType;
return this;
}
/// <summary>
/// This is only supported when using MQTTv5.
/// </summary>
public MqttApplicationMessageBuilder WithResponseTopic(string responseTopic)
{
_responseTopic = responseTopic;
return this;
}
/// <summary>
/// This is only supported when using MQTTv5.
/// </summary>
public MqttApplicationMessageBuilder WithCorrelationData(byte[] correlationData)
{
_correlationData = correlationData;
return this;
}
/// <summary>
/// This is only supported when using MQTTv5.
/// </summary>
public MqttApplicationMessageBuilder WithTopicAlias(ushort topicAlias)
{
_topicAlias = topicAlias;
return this;
}
/// <summary>
/// This is only supported when using MQTTv5.
/// </summary>
public MqttApplicationMessageBuilder WithSubscriptionIdentifier(uint subscriptionIdentifier)
{
_subscriptionIdentifier = subscriptionIdentifier;
return this;
}
/// <summary>
/// This is only supported when using MQTTv5.
/// </summary>
public MqttApplicationMessageBuilder WithMessageExpiryInterval(uint messageExpiryInterval)
{
_messageExpiryInterval = messageExpiryInterval;
return this;
}
/// <summary>
/// This is only supported when using MQTTv5.
/// </summary>
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;
}
}
}
+14
View File
@@ -0,0 +1,14 @@
using MQTTnet.Protocol;
namespace MQTTnet.Packets
{
/// <summary>
/// Added in MQTTv5.0.0.
/// </summary>
public class MqttAuthPacket : MqttBasePacket
{
public MqttAuthenticateReasonCode ReasonCode { get; set; }
public MqttAuthPacketProperties Properties { get; set; }
}
}
@@ -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<MqttUserProperty> UserProperties { get; } = new List<MqttUserProperty>();
}
}
+14 -2
View File
@@ -4,13 +4,25 @@ namespace MQTTnet.Packets
{
public class MqttConnAckPacket : MqttBasePacket
{
public MqttConnectReturnCode? ReturnCode { get; set; }
#region Added in MQTTv3.1.1
public bool IsSessionPresent { get; set; }
public MqttConnectReturnCode ConnectReturnCode { get; set; }
#endregion
#region Added in MQTTv5.0.0
public MqttConnectReasonCode? ReasonCode { get; set; }
public MqttConnAckPacketProperties Properties { get; set; }
#endregion
public override string ToString()
{
return "ConnAck: [ConnectReturnCode=" + ConnectReturnCode + "] [IsSessionPresent=" + IsSessionPresent + "]";
return string.Concat("ConnAck: [ReturnCode=", ReturnCode, "] [ReasonCode=", ReasonCode, "] [IsSessionPresent=", IsSessionPresent, "]");
}
}
}
@@ -0,0 +1,39 @@
using System.Collections.Generic;
namespace MQTTnet.Packets
{
public class MqttConnAckPacketProperties
{
public uint? SessionExpiryInterval { get; set; }
public ushort? ReceiveMaximum { get; set; }
public bool? RetainAvailable { get; set; }
public uint? MaximumPacketSize { get; set; }
public string AssignedClientIdentifier { get; set; }
public ushort? TopicAliasMaximum { get; set; }
public string ReasonString { get; set; }
public List<MqttUserProperty> UserProperties { get; } = new List<MqttUserProperty>();
public bool? WildcardSubscriptionAvailable { get; set; }
public bool? SubscriptionIdentifiersAvailable { get; set; }
public bool? SharedSubscriptionAvailable { get; set; }
public ushort? ServerKeepAlive { get; set; }
public string ResponseInformation { get; set; }
public string ServerReference { get; set; }
public string AuthenticationMethod { get; set; }
public byte[] AuthenticationData { get; set; }
}
}
+12 -5
View File
@@ -1,10 +1,10 @@
using MQTTnet.Serializer;
namespace MQTTnet.Packets
namespace MQTTnet.Packets
{
public class MqttConnectPacket : MqttBasePacket
{
public MqttProtocolVersion ProtocolVersion { get; set; }
public string ProtocolName { get; set; }
public byte? ProtocolLevel { get; set; }
public string ClientId { get; set; }
@@ -14,13 +14,20 @@ namespace MQTTnet.Packets
public ushort KeepAlivePeriod { get; set; }
// Also called "Clean Start" in MQTTv5.
public bool CleanSession { get; set; }
public MqttApplicationMessage WillMessage { get; set; }
#region Added in MQTTv5.0.0
public MqttConnectPacketProperties Properties { get; set; }
#endregion
public override string ToString()
{
return "Connect: [ClientId=" + ClientId + "] [Username=" + Username + "] [Password=" + Password + "] [KeepAlivePeriod=" + KeepAlivePeriod + "] [CleanSession=" + CleanSession + "]";
return string.Concat("Connect: [ProtocolLevel=", ProtocolLevel, "] [ClientId=", ClientId, "] [Username=", Username, "] [Password=", Password, "] [KeepAlivePeriod=", KeepAlivePeriod, "] [CleanSession=", CleanSession, "]");
}
}
}
@@ -0,0 +1,27 @@
using System.Collections.Generic;
namespace MQTTnet.Packets
{
public class MqttConnectPacketProperties
{
public uint? WillDelayInterval { get; set; }
public uint? SessionExpiryInterval { get; set; }
public string AuthenticationMethod { get; set; }
public byte[] AuthenticationData { get; set; }
public bool? RequestProblemInformation { get; set; }
public bool? RequestResponseInformation { get; set; }
public ushort? ReceiveMaximum { get; set; }
public ushort? TopicAliasMaximum { get; set; }
public uint? MaximumPacketSize { get; set; }
public List<MqttUserProperty> UserProperties { get; } = new List<MqttUserProperty>();
}
}
+12 -2
View File
@@ -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, "]");
}
}
}
@@ -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<MqttUserProperty> UserProperties { get; } = new List<MqttUserProperty>();
public string ServerReference { get; set; }
}
}
+12 -2
View File
@@ -1,10 +1,20 @@
namespace MQTTnet.Packets
using MQTTnet.Protocol;
namespace MQTTnet.Packets
{
public class MqttPubAckPacket : MqttBasePublishPacket
{
#region Added in MQTTv5
public MqttPubAckReasonCode? ReasonCode { get; set; }
public MqttPubAckPacketProperties Properties { get; set; }
#endregion
public override string ToString()
{
return $"PubAck [PacketIdentifier={PacketIdentifier}]";
return string.Concat("PubAck: [PacketIdentifier=", PacketIdentifier, "] [ReasonCode=", ReasonCode, "]");
}
}
}

Some files were not shown because too many files have changed in this diff Show More