Compare commits

...
18 Commits
Author SHA1 Message Date
christian 7715bf37dc Remove package suffix for final release version 2024-12-30 11:54:00 +01:00
Christian 9dbe1e4b12 Fix support for enhanced authentication (#2132)
* Implement enhanced authentication for client and server.

* Update samples

* Add abstraction layer

* Merge master

* Use dedicated class for public API.

* Update release notes

* Rename methods to fit naming scheme

* Fix unit tests and samples
2024-12-25 14:17:20 +01:00
christian f13b3eb6bb Update release notes 2024-12-23 12:17:00 +01:00
Anton SmolkovandAnton Smolkov 8eb884fa5d Add ability to get session from server by client id (#2131)
Co-authored-by: Anton Smolkov <anton.smolkov@idp.zyfra.com>
2024-12-23 12:05:09 +01:00
Victornorandchristian 9f81a2bb71 Add deflate compressions options to MqttClientWebSocketOptionsBuilder (#2127)
* fix: unused tryPrivate parameter in WithTryPrivate

* Fix wrong usage of try private

* feat: add option to provide deflate compression options to WebSocketOptionsBuilder

---------

Co-authored-by: christian <6939810+chkr1011@users.noreply.github.com>
2024-12-23 11:21:46 +01:00
老九andchristian e2f8d0f0ac Added UserName property to various Server EventArgs. (#2123)
* Added UserName property to various Server EventArgs.

* InjectedMqttApplicationMessage: Add SenderUserName property.

* Reformat code

* Reformat code

---------

Co-authored-by: christian <6939810+chkr1011@users.noreply.github.com>
2024-12-10 19:44:38 +01:00
Victornorandchristian 981ea02149 Unused parameter in WithTryPrivate causing client to send incorrect version byte in header. (#2125)
* fix: unused tryPrivate parameter in WithTryPrivate

* Fix wrong usage of try private

---------

Co-authored-by: christian <6939810+chkr1011@users.noreply.github.com>
2024-12-10 19:37:29 +01:00
Christian 8566fd5395 Replace all _DataTestMethod_ (#2119)
* Replace all DataTestMethod with TestMethod.

* Update test nugets
2024-12-03 11:40:06 +01:00
老九andchristian 8089c6bdbc Added EndPoint RemoteEndPoint {get;} property to channel (#2111)
* Added EndPoint RemoteEndPoint{get;} property

* Reformat code

* Reformat code

---------

Co-authored-by: christian <6939810+chkr1011@users.noreply.github.com>
2024-12-02 11:52:24 +01:00
christian ef5b6c9b32 Merge remote-tracking branch 'origin/master' 2024-12-02 11:22:11 +01:00
christian 4aa4a12e47 Update release notes 2024-12-02 10:40:05 +01:00
老九 6bf2b4eaca Cannot set any properties of tcpOptions when with WebSocketServer. (#2118) 2024-12-02 10:24:03 +01:00
老九 6ef6b57064 Improve the compatibility of MqttClientOptionsBuilder.WithConnectionUri() (#2107)
* Improve the compatibility of MqttClientOptionsBuilder.WithConnectionUri() method.

* Force UseTls to true for secure schemes.

* Add support for UnixDomainSocket.
2024-12-01 14:10:58 +01:00
老九 7d4027958c Set the default value of SslProtocol to SslProtocols.None. (#2106)
* Set the default value of SslProtocol to SslProtocols.None.

* Add summary of SslProtocol.
2024-12-01 14:06:19 +01:00
老九 bdb74642c2 MqttClientFactory: Fix CreateLowLevelMqttClient not using method parameters (#2110)
* Fix CreateLowLevelMqttClient not using method parameters.

* Remove UseClientAdapterFactory()
2024-11-30 13:46:36 +01:00
Andrea Vouk 63d85184e1 Convert null checks to modern ThrowIfNull checks (#2089)
Convert null check to modern ThrowIfNull checks
2024-10-26 11:41:58 +02:00
Christian 0506857f0d Set max pending mesages per client to 1000 (#2096)
Set max pending mesages per client to 1000.
2024-10-26 11:41:25 +02:00
christian 6417baa081 Fix nuget publish 2024-10-22 11:08:29 +02:00
134 changed files with 1815 additions and 1784 deletions
+3 -2
View File
@@ -4,7 +4,7 @@ on: [push, pull_request]
env: env:
VERSION: "5.0.0.${{github.run_number}}" VERSION: "5.0.0.${{github.run_number}}"
PACKAGE_SUFFIX: "-RC" PACKAGE_SUFFIX: ""
jobs: jobs:
build: build:
@@ -107,7 +107,8 @@ jobs:
run: dotnet nuget push ${{ github.workspace }}/nugets/**/*.nupkg -k ${{ secrets.MYGET_API_KEY }} -s https://www.myget.org/F/mqttnet/api/v3/index.json --skip-duplicate run: dotnet nuget push ${{ github.workspace }}/nugets/**/*.nupkg -k ${{ secrets.MYGET_API_KEY }} -s https://www.myget.org/F/mqttnet/api/v3/index.json --skip-duplicate
publish-nuget: publish-nuget:
if: ${{ github.event_name == 'published' }} # Tag schema is 'v5.0.0-rc' etc.
if: startsWith(github.ref, 'refs/tags/v')
needs: [sign, test] needs: [sign, test]
runs-on: ubuntu-latest runs-on: ubuntu-latest
+477 -418
View File
@@ -8,431 +8,17 @@
using System.Security.Authentication; using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Text;
using MQTTnet.Formatter; using MQTTnet.Formatter;
using MQTTnet.Protocol;
using MQTTnet.Samples.Helpers; using MQTTnet.Samples.Helpers;
using MQTTnet.Server;
using MQTTnet.Server.EnhancedAuthentication;
namespace MQTTnet.Samples.Client; namespace MQTTnet.Samples.Client;
public static class Client_Connection_Samples public static class Client_Connection_Samples
{ {
public static async Task Clean_Disconnect()
{
/*
* This sample disconnects in a clean way. This will send a MQTT DISCONNECT packet
* to the server and close the connection afterwards.
*
* See sample _Connect_Client_ for more details.
*/
var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
// This will send the DISCONNECT packet. Calling _Dispose_ without DisconnectAsync the
// connection is closed in a "not clean" way. See MQTT specification for more details.
await mqttClient.DisconnectAsync(new MqttClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.NormalDisconnection).Build());
}
}
public static async Task Connect_Client()
{
/*
* This sample creates a simple MQTT client and connects to a public broker.
*
* Always dispose the client when it is no longer used.
* The default version of MQTT is 3.1.1.
*/
var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
// Use builder classes where possible in this project.
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
// This will throw an exception if the server is not available.
// The result from this message returns additional data which was sent
// from the server. Please refer to the MQTT protocol specification for details.
var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
Console.WriteLine("The MQTT client is connected.");
response.DumpToConsole();
// Send a clean disconnect to the server by calling _DisconnectAsync_. Without this the TCP connection
// gets dropped and the server will handle this as a non clean disconnect (see MQTT spec for details).
var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder().Build();
await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
}
}
public static async Task Connect_Client_Timeout()
{
/*
* This sample creates a simple MQTT client and connects to an invalid broker using a timeout.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/
var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").Build();
try
{
using (var timeoutToken = new CancellationTokenSource(TimeSpan.FromSeconds(1)))
{
await mqttClient.ConnectAsync(mqttClientOptions, timeoutToken.Token);
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Timeout while connecting.");
}
}
}
public static async Task Connect_Client_Using_MQTTv5()
{
/*
* This sample creates a simple MQTT client and connects to a public broker using MQTTv5.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/
var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").WithProtocolVersion(MqttProtocolVersion.V500).Build();
// In MQTTv5 the response contains much more information.
var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
Console.WriteLine("The MQTT client is connected.");
response.DumpToConsole();
}
}
public static async Task Connect_Client_Using_TLS_1_2()
{
/*
* This sample creates a simple MQTT client and connects to a public broker using TLS 1.2 encryption.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/
var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("mqtt.fluux.io")
.WithTlsOptions(
o =>
{
// The used public broker sometimes has invalid certificates. This sample accepts all
// certificates. This should not be used in live environments.
o.WithCertificateValidationHandler(_ => true);
// The default value is determined by the OS. Set manually to force version.
o.WithSslProtocols(SslProtocols.Tls12);
})
.Build();
using (var timeout = new CancellationTokenSource(5000))
{
await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
Console.WriteLine("The MQTT client is connected.");
}
}
}
public static async Task Connect_Client_Using_WebSockets()
{
/*
* This sample creates a simple MQTT client and connects to a public broker using a WebSocket connection.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/
var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("broker.hivemq.com:8000/mqtt")).Build();
var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
Console.WriteLine("The MQTT client is connected.");
response.DumpToConsole();
}
}
public static async Task Connect_Client_With_TLS_Encryption()
{
/*
* This sample creates a simple MQTT client and connects to a public broker with enabled TLS encryption.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/
var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883)
.WithTlsOptions(
o => o.WithCertificateValidationHandler(
// The used public broker sometimes has invalid certificates. This sample accepts all
// certificates. This should not be used in live environments.
_ => true))
.Build();
// In MQTTv5 the response contains much more information.
using (var timeout = new CancellationTokenSource(5000))
{
var response = await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
Console.WriteLine("The MQTT client is connected.");
response.DumpToConsole();
}
}
}
public static async Task Connect_With_Amazon_AWS()
{
/*
* This sample creates a simple MQTT client and connects to an Amazon Web Services broker.
*
* The broker requires special settings which are set here.
*/
var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("amazon.web.services.broker")
// Disabling packet fragmentation is very important!
.WithoutPacketFragmentation()
.Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
Console.WriteLine("The MQTT client is connected.");
await mqttClient.DisconnectAsync();
}
}
public static async Task Disconnect_Clean()
{
/*
* This sample disconnects from the server with sending a DISCONNECT packet.
* This way of disconnecting is treated as a clean disconnect which will not
* trigger sending the last will etc.
*/
var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
// Calling _DisconnectAsync_ will send a DISCONNECT packet before closing the connection.
// Using a reason code requires MQTT version 5.0.0!
await mqttClient.DisconnectAsync(MqttClientDisconnectOptionsReason.ImplementationSpecificError);
}
}
public static async Task Disconnect_Non_Clean()
{
/*
* This sample disconnects from the server without sending a DISCONNECT packet.
* This way of disconnecting is treated as a non clean disconnect which will
* trigger sending the last will etc.
*/
var mqttFactory = new MqttClientFactory();
var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
// Calling _Dispose_ or use of a _using_ statement will close the transport connection
// without sending a DISCONNECT packet to the server.
mqttClient.Dispose();
}
public static async Task Inspect_Certificate_Validation_Errors()
{
/*
* This sample prints the certificate information while connection. This data can be used to decide whether a connection is secure or not
* including the reason for that status.
*/
var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("mqtt.fluux.io", 8883)
.WithTlsOptions(
o =>
{
o.WithCertificateValidationHandler(
eventArgs =>
{
eventArgs.Certificate.Subject.DumpToConsole();
eventArgs.Certificate.GetExpirationDateString().DumpToConsole();
eventArgs.Chain.ChainPolicy.RevocationMode.DumpToConsole();
eventArgs.Chain.ChainStatus.DumpToConsole();
eventArgs.SslPolicyErrors.DumpToConsole();
return true;
});
})
.Build();
// In MQTTv5 the response contains much more information.
using (var timeout = new CancellationTokenSource(5000))
{
await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
}
}
}
public static async Task Ping_Server()
{
/*
* This sample sends a PINGREQ packet to the server and waits for a reply.
*
* This is only supported in MQTTv5.0.0+.
*/
var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
// This will throw an exception if the server does not reply.
await mqttClient.PingAsync(CancellationToken.None);
Console.WriteLine("The MQTT server replied to the ping request.");
}
}
public static async Task Reconnect_Using_Event()
{
/*
* This sample shows how to reconnect when the connection was dropped.
* This approach uses one of the events from the client.
* This approach has a risk of dead locks! Consider using the timer approach (see sample).
*/
var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
mqttClient.DisconnectedAsync += async e =>
{
if (e.ClientWasConnected)
{
// Use the current options as the new options.
await mqttClient.ConnectAsync(mqttClient.Options);
}
};
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
}
}
public static void Reconnect_Using_Timer()
{
/*
* This sample shows how to reconnect when the connection was dropped.
* This approach uses a custom Task/Thread which will monitor the connection status.
* This is the recommended way but requires more custom code!
*/
var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
_ = Task.Run(
async () =>
{
// User proper cancellation and no while(true).
while (true)
{
try
{
// This code will also do the very first connect! So no call to _ConnectAsync_ is required in the first place.
if (!await mqttClient.TryPingAsync())
{
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
// Subscribe to topics when session is clean etc.
Console.WriteLine("The MQTT client is connected.");
}
}
catch
{
// Handle the exception properly (logging etc.).
}
finally
{
// Check the connection state every 5 seconds and perform a reconnect if required.
await Task.Delay(TimeSpan.FromSeconds(5));
}
}
});
Console.WriteLine("Press <Enter> to exit");
Console.ReadLine();
}
}
public static async Task ConnectTls_WithCaFile()
{
var mqttFactory = new MqttClientFactory();
X509Certificate2Collection caChain = new X509Certificate2Collection();
caChain.ImportFromPem(mosquitto_org); // from https://test.mosquitto.org/ssl/mosquitto.org.crt
using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("test.mosquitto.org", 8883)
.WithTlsOptions(new MqttClientTlsOptionsBuilder()
.WithTrustChain(caChain)
.Build())
.Build();
var connAck = await mqttClient.ConnectAsync(mqttClientOptions);
Console.WriteLine("Connected to test.moquitto.org:8883 with CaFile mosquitto.org.crt: " + connAck.ResultCode);
}
}
const string mosquitto_org = @" const string mosquitto_org = @"
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIEAzCCAuugAwIBAgIUBY1hlCGvdj4NhBXkZ/uLUZNILAwwDQYJKoZIhvcNAQEL MIIEAzCCAuugAwIBAgIUBY1hlCGvdj4NhBXkZ/uLUZNILAwwDQYJKoZIhvcNAQEL
@@ -460,4 +46,477 @@ m/XriWr/Cq4h/JfB7NTsezVslgkBaoU=
-----END CERTIFICATE----- -----END CERTIFICATE-----
"; ";
public static async Task Clean_Disconnect()
{
/*
* This sample disconnects in a clean way. This will send a MQTT DISCONNECT packet
* to the server and close the connection afterward.
*
* See sample _Connect_Client_ for more details.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
// This will send the DISCONNECT packet. Calling _Dispose_ without DisconnectAsync the
// connection is closed in a "not clean" way. See MQTT specification for more details.
await mqttClient.DisconnectAsync(new MqttClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.NormalDisconnection).Build());
}
public static async Task Connect()
{
/*
* This sample creates a simple MQTT client and connects to a public broker.
*
* Always dispose the client when it is no longer used.
* The default version of MQTT is 3.1.1.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
// Use builder classes where possible in this project.
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
// This will throw an exception if the server is not available.
// The result from this message returns additional data which was sent
// from the server. Please refer to the MQTT protocol specification for details.
var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
Console.WriteLine("The MQTT client is connected.");
response.DumpToConsole();
// Send a clean disconnect to the server by calling _DisconnectAsync_. Without this the TCP connection
// gets dropped and the server will handle this as a non clean disconnect (see MQTT spec for details).
var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder().Build();
await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
}
public static async Task Connect_Using_Enhanced_Authentication()
{
/*
* This sample uses enhanced authentication (Kerberos) when creating the connection.
*/
/*
* Server part...
*/
var mqttServerFactory = new MqttServerFactory();
var serverOptions = mqttServerFactory.CreateServerOptionsBuilder().WithDefaultEndpoint().Build();
var server = mqttServerFactory.CreateMqttServer(serverOptions);
server.ValidatingConnectionAsync += async args =>
{
if (args.AuthenticationMethod == "GS2-KRB5")
{
var result = await args.ExchangeEnhancedAuthenticationAsync(new ExchangeEnhancedAuthenticationOptions(), args.CancellationToken);
Console.WriteLine($"Received AUTH data from client: {Encoding.UTF8.GetString(result.AuthenticationData)}");
var authOptions = mqttServerFactory.CreateExchangeExtendedAuthenticationOptionsBuilder().WithAuthenticationData("reply context token").Build();
result = await args.ExchangeEnhancedAuthenticationAsync(authOptions, args.CancellationToken);
Console.WriteLine($"Received AUTH data from client: {Encoding.UTF8.GetString(result.AuthenticationData)}");
args.ResponseAuthenticationData = "outcome of authentication"u8.ToArray();
// Authentication DONE!
args.ReasonCode = MqttConnectReasonCode.Success; // Also the default!
}
else
{
args.ReasonCode = MqttConnectReasonCode.BadAuthenticationMethod;
}
};
await server.StartAsync();
/*
* Client part...
*/
var mqttClientFactory = new MqttClientFactory();
// Use Kerberos sample from the MQTT RFC.
var kerberosAuthenticationHandler = new SampleClientKerberosAuthenticationHandler();
var clientOptions = mqttClientFactory.CreateClientOptionsBuilder()
.WithTcpServer("localhost")
.WithProtocolVersion(MqttProtocolVersion.V500)
.WithEnhancedAuthenticationHandler(kerberosAuthenticationHandler)
.WithEnhancedAuthentication("GS2-KRB5")
.Build();
var client = mqttClientFactory.CreateMqttClient();
var result = await client.ConnectAsync(clientOptions);
Console.WriteLine($"Client connect result: {result.ResultCode}");
}
public static async Task Connect_Using_MQTTv5()
{
/*
* This sample creates a simple MQTT client and connects to a public broker using MQTTv5.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").WithProtocolVersion(MqttProtocolVersion.V500).Build();
// In MQTTv5 the response contains much more information.
var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
Console.WriteLine("The MQTT client is connected.");
response.DumpToConsole();
}
public static async Task Connect_Using_TLS_1_2()
{
/*
* This sample creates a simple MQTT client and connects to a public broker using TLS 1.2 encryption.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("mqtt.fluux.io")
.WithTlsOptions(
o =>
{
// The used public broker sometimes has invalid certificates. This sample accepts all
// certificates. This should not be used in live environments.
o.WithCertificateValidationHandler(_ => true);
// The default value is determined by the OS. Set manually to force version.
o.WithSslProtocols(SslProtocols.Tls12);
})
.Build();
using var timeout = new CancellationTokenSource(5000);
await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
Console.WriteLine("The MQTT client is connected.");
}
public static async Task Connect_Using_TLS_Encryption()
{
/*
* This sample creates a simple MQTT client and connects to a public broker with enabled TLS encryption.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883)
.WithTlsOptions(
o => o.WithCertificateValidationHandler(
// The used public broker sometimes has invalid certificates. This sample accepts all
// certificates. This should not be used in live environments.
_ => true))
.Build();
// In MQTTv5 the response contains much more information.
using var timeout = new CancellationTokenSource(5000);
var response = await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
Console.WriteLine("The MQTT client is connected.");
response.DumpToConsole();
}
public static async Task Connect_Using_TLS_With_CA_File()
{
var mqttFactory = new MqttClientFactory();
var caChain = new X509Certificate2Collection();
caChain.ImportFromPem(mosquitto_org); // from https://test.mosquitto.org/ssl/mosquitto.org.crt
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883)
.WithTlsOptions(new MqttClientTlsOptionsBuilder().WithTrustChain(caChain).Build())
.Build();
var connAck = await mqttClient.ConnectAsync(mqttClientOptions);
Console.WriteLine("Connected to test.moquitto.org:8883 with CaFile mosquitto.org.crt: " + connAck.ResultCode);
}
public static async Task Connect_Using_WebSockets()
{
/*
* This sample creates a simple MQTT client and connects to a public broker using a WebSocket connection.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("broker.hivemq.com:8000/mqtt")).Build();
var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
Console.WriteLine("The MQTT client is connected.");
response.DumpToConsole();
}
public static async Task Connect_With_Amazon_AWS()
{
/*
* This sample creates a simple MQTT client and connects to an Amazon Web Services broker.
*
* The broker requires special settings which are set here.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("amazon.web.services.broker")
// Disabling packet fragmentation is very important!
.WithoutPacketFragmentation()
.Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
Console.WriteLine("The MQTT client is connected.");
await mqttClient.DisconnectAsync();
}
public static async Task Disconnect_Clean()
{
/*
* This sample disconnects from the server with sending a DISCONNECT packet.
* This way of disconnecting is treated as a clean disconnect which will not
* trigger sending the last will etc.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
// Calling _DisconnectAsync_ will send a DISCONNECT packet before closing the connection.
// Using a reason code requires MQTT version 5.0.0!
await mqttClient.DisconnectAsync(MqttClientDisconnectOptionsReason.ImplementationSpecificError);
}
public static async Task Disconnect_Non_Clean()
{
/*
* This sample disconnects from the server without sending a DISCONNECT packet.
* This way of disconnecting is treated as a non-clean disconnect which will
* trigger sending the last will etc.
*/
var mqttFactory = new MqttClientFactory();
var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
// Calling _Dispose_ or use of a _using_ statement will close the transport connection
// without sending a DISCONNECT packet to the server.
mqttClient.Dispose();
}
public static async Task Inspect_Certificate_Validation_Errors()
{
/*
* This sample prints the certificate information while connection. This data can be used to decide whether a connection is secure or not
* including the reason for that status.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("mqtt.fluux.io", 8883)
.WithTlsOptions(
o =>
{
o.WithCertificateValidationHandler(
eventArgs =>
{
eventArgs.Certificate.Subject.DumpToConsole();
eventArgs.Certificate.GetExpirationDateString().DumpToConsole();
eventArgs.Chain.ChainPolicy.RevocationMode.DumpToConsole();
eventArgs.Chain.ChainStatus.DumpToConsole();
eventArgs.SslPolicyErrors.DumpToConsole();
return true;
});
})
.Build();
// In MQTTv5 the response contains much more information.
using var timeout = new CancellationTokenSource(5000);
await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
}
public static async Task Ping_Server()
{
/*
* This sample sends a PINGREQ packet to the server and waits for a reply.
*
* This is only supported in MQTTv5.0.0+.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
// This will throw an exception if the server does not reply.
await mqttClient.PingAsync(CancellationToken.None);
Console.WriteLine("The MQTT server replied to the ping request.");
}
public static async Task Reconnect_Using_Event()
{
/*
* This sample shows how to reconnect when the connection was dropped.
* This approach uses one of the events from the client.
* This approach has a risk of deadlocks! Consider using the timer approach (see sample).
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
mqttClient.DisconnectedAsync += async e =>
{
if (e.ClientWasConnected)
{
// Use the current options as the new options.
await mqttClient.ConnectAsync(mqttClient.Options);
}
};
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
}
public static void Reconnect_Using_Timer()
{
/*
* This sample shows how to reconnect when the connection was dropped.
* This approach uses a custom Task/Thread which will monitor the connection status.
* This is the recommended way but requires more custom code!
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
_ = Task.Run(
async () =>
{
// User proper cancellation and no while(true).
while (true)
{
try
{
// This code will also do the very first connect! So no call to _ConnectAsync_ is required in the first place.
if (!await mqttClient.TryPingAsync())
{
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
// Subscribe to topics when session is clean etc.
Console.WriteLine("The MQTT client is connected.");
}
}
catch
{
// Handle the exception properly (logging etc.).
}
finally
{
// Check the connection state every 5 seconds and perform a reconnect if required.
await Task.Delay(TimeSpan.FromSeconds(5));
}
}
});
Console.WriteLine("Press <Enter> to exit");
Console.ReadLine();
}
public static async Task Timeout()
{
/*
* This sample creates a simple MQTT client and connects to an invalid broker using a timeout.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").Build();
try
{
using var timeoutToken = new CancellationTokenSource(TimeSpan.FromSeconds(1));
await mqttClient.ConnectAsync(mqttClientOptions, timeoutToken.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Timeout while connecting.");
}
}
class SampleClientKerberosAuthenticationHandler : IMqttEnhancedAuthenticationHandler
{
public async Task HandleEnhancedAuthenticationAsync(MqttEnhancedAuthenticationEventArgs eventArgs)
{
if (eventArgs.AuthenticationMethod != "GS2-KRB5")
{
throw new InvalidOperationException("Wrong authentication method");
}
var sendOptions = new SendMqttEnhancedAuthenticationDataOptions
{
Data = "initial context token"u8.ToArray()
};
await eventArgs.SendAsync(sendOptions);
var response = await eventArgs.ReceiveAsync(CancellationToken.None);
Console.WriteLine($"Received AUTH data from server: {Encoding.UTF8.GetString(response.AuthenticationData)}");
// No further data is required, but we have to fulfil the exchange.
sendOptions = new SendMqttEnhancedAuthenticationDataOptions
{
Data = []
};
await eventArgs.SendAsync(sendOptions, CancellationToken.None);
}
}
} }
@@ -102,10 +102,7 @@ public static class Server_Retained_Messages_Samples
public static MqttRetainedMessageModel Create(MqttApplicationMessage message) public static MqttRetainedMessageModel Create(MqttApplicationMessage message)
{ {
if (message == null) ArgumentNullException.ThrowIfNull(message);
{
throw new ArgumentNullException(nameof(message));
}
return new MqttRetainedMessageModel return new MqttRetainedMessageModel
{ {
@@ -12,10 +12,7 @@ namespace MQTTnet.AspNetCore
{ {
public static void MapMqtt(this IEndpointRouteBuilder endpoints, string pattern) public static void MapMqtt(this IEndpointRouteBuilder endpoints, string pattern)
{ {
if (endpoints == null) ArgumentNullException.ThrowIfNull(endpoints);
{
throw new ArgumentNullException(nameof(endpoints));
}
endpoints.MapConnectionHandler<MqttConnectionHandler>(pattern, options => endpoints.MapConnectionHandler<MqttConnectionHandler>(pattern, options =>
{ {
@@ -61,21 +61,21 @@ public sealed class MqttConnectionContext : IMqttChannelAdapter
} }
} }
public string Endpoint public EndPoint RemoteEndPoint
{ {
get get
{ {
// mqtt over tcp // mqtt over tcp
if (_connection.RemoteEndPoint != null) if (_connection.RemoteEndPoint != null)
{ {
return _connection.RemoteEndPoint.ToString(); return _connection.RemoteEndPoint;
} }
// mqtt over websocket // mqtt over websocket
var httpFeature = _connection.Features.Get<IHttpConnectionFeature>(); var httpFeature = _connection.Features.Get<IHttpConnectionFeature>();
if (httpFeature?.RemoteIpAddress != null) if (httpFeature?.RemoteIpAddress != null)
{ {
return new IPEndPoint(httpFeature.RemoteIpAddress, httpFeature.RemotePort).ToString(); return new IPEndPoint(httpFeature.RemoteIpAddress, httpFeature.RemotePort);
} }
return null; return null;
@@ -13,10 +13,7 @@ public static class MqttSubProtocolSelector
{ {
public static string SelectSubProtocol(HttpRequest request) public static string SelectSubProtocol(HttpRequest request)
{ {
if (request == null) ArgumentNullException.ThrowIfNull(request);
{
throw new ArgumentNullException(nameof(request));
}
string subProtocol = null; string subProtocol = null;
if (request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues)) if (request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
@@ -29,10 +26,7 @@ public static class MqttSubProtocolSelector
public static string SelectSubProtocol(IList<string> requestedSubProtocolValues) public static string SelectSubProtocol(IList<string> requestedSubProtocolValues)
{ {
if (requestedSubProtocolValues == null) ArgumentNullException.ThrowIfNull(requestedSubProtocolValues);
{
throw new ArgumentNullException(nameof(requestedSubProtocolValues));
}
// Order the protocols to also match "mqtt", "mqttv-3.1", "mqttv-3.11" etc. // Order the protocols to also match "mqtt", "mqttv-3.1", "mqttv-3.11" etc.
return requestedSubProtocolValues.OrderByDescending(p => p.Length).FirstOrDefault(p => p.ToLower().StartsWith("mqtt")); return requestedSubProtocolValues.OrderByDescending(p => p.Length).FirstOrDefault(p => p.ToLower().StartsWith("mqtt"));
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; using System;
using System.Net;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@@ -26,12 +27,10 @@ public sealed class MqttWebSocketServerAdapter : IMqttServerAdapter
public async Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext) public async Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
{ {
if (webSocket == null) ArgumentNullException.ThrowIfNull(webSocket);
{
throw new ArgumentNullException(nameof(webSocket));
}
var endpoint = $"{httpContext.Connection.RemoteIpAddress}:{httpContext.Connection.RemotePort}"; var remoteAddress = httpContext.Connection.RemoteIpAddress;
var remoteEndPoint = remoteAddress == null ? null : new IPEndPoint(remoteAddress, httpContext.Connection.RemotePort);
var clientCertificate = await httpContext.Connection.GetClientCertificateAsync().ConfigureAwait(false); var clientCertificate = await httpContext.Connection.GetClientCertificateAsync().ConfigureAwait(false);
try try
@@ -42,7 +41,7 @@ public sealed class MqttWebSocketServerAdapter : IMqttServerAdapter
if (clientHandler != null) if (clientHandler != null)
{ {
var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535)); var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535));
var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection, clientCertificate); var channel = new MqttWebSocketChannel(webSocket, remoteEndPoint, isSecureConnection, clientCertificate);
using (var channelAdapter = new MqttChannelAdapter(channel, formatter, _logger)) using (var channelAdapter = new MqttChannelAdapter(channel, formatter, _logger))
{ {
@@ -22,10 +22,7 @@ public static class ReaderExtensions
out SequencePosition observed, out SequencePosition observed,
out int bytesRead) out int bytesRead)
{ {
if (formatter == null) ArgumentNullException.ThrowIfNull(formatter);
{
throw new ArgumentNullException(nameof(formatter));
}
packet = null; packet = null;
consumed = input.Start; consumed = input.Start;
@@ -16,15 +16,8 @@ public static class ServiceCollectionExtensions
{ {
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, MqttServerOptions options) public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, MqttServerOptions options)
{ {
if (services == null) ArgumentNullException.ThrowIfNull(services);
{ ArgumentNullException.ThrowIfNull(options);
throw new ArgumentNullException(nameof(services));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
services.AddSingleton(options); services.AddSingleton(options);
services.AddHostedMqttServer(); services.AddHostedMqttServer();
@@ -34,10 +27,7 @@ public static class ServiceCollectionExtensions
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, Action<MqttServerOptionsBuilder> configure) public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, Action<MqttServerOptionsBuilder> configure)
{ {
if (services == null) ArgumentNullException.ThrowIfNull(services);
{
throw new ArgumentNullException(nameof(services));
}
var serverOptionsBuilder = new MqttServerOptionsBuilder(); var serverOptionsBuilder = new MqttServerOptionsBuilder();
@@ -61,10 +51,7 @@ public static class ServiceCollectionExtensions
public static IServiceCollection AddHostedMqttServerWithServices(this IServiceCollection services, Action<AspNetMqttServerOptionsBuilder> configure) public static IServiceCollection AddHostedMqttServerWithServices(this IServiceCollection services, Action<AspNetMqttServerOptionsBuilder> configure)
{ {
if (services == null) ArgumentNullException.ThrowIfNull(services);
{
throw new ArgumentNullException(nameof(services));
}
services.AddSingleton( services.AddSingleton(
s => s =>
@@ -89,20 +76,14 @@ public static class ServiceCollectionExtensions
public static void AddMqttLogger(this IServiceCollection services, IMqttNetLogger logger) public static void AddMqttLogger(this IServiceCollection services, IMqttNetLogger logger)
{ {
if (services == null) ArgumentNullException.ThrowIfNull(services);
{
throw new ArgumentNullException(nameof(services));
}
services.AddSingleton(logger); services.AddSingleton(logger);
} }
public static IServiceCollection AddMqttServer(this IServiceCollection serviceCollection, Action<MqttServerOptionsBuilder> configure = null) public static IServiceCollection AddMqttServer(this IServiceCollection serviceCollection, Action<MqttServerOptionsBuilder> configure = null)
{ {
if (serviceCollection is null) ArgumentNullException.ThrowIfNull(serviceCollection);
{
throw new ArgumentNullException(nameof(serviceCollection));
}
serviceCollection.AddMqttConnectionHandler(); serviceCollection.AddMqttConnectionHandler();
serviceCollection.AddHostedMqttServer(configure); serviceCollection.AddHostedMqttServer(configure);
@@ -18,7 +18,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.12"/> <PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -145,7 +145,7 @@ namespace MQTTnet.Benchmarks
out SequencePosition observed, out SequencePosition observed,
out int bytesRead) out int bytesRead)
{ {
if (formatter == null) throw new ArgumentNullException(nameof(formatter)); ArgumentNullException.ThrowIfNull(formatter);
packet = null; packet = null;
consumed = input.Start; consumed = input.Start;
@@ -15,6 +15,7 @@ using MQTTnet.Formatter.V3;
using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Jobs;
using MQTTnet.Diagnostics.Logger; using MQTTnet.Diagnostics.Logger;
using System.Buffers; using System.Buffers;
using System.Net;
namespace MQTTnet.Benchmarks namespace MQTTnet.Benchmarks
{ {
@@ -76,7 +77,7 @@ namespace MQTTnet.Benchmarks
_position = _buffer.Offset; _position = _buffer.Offset;
} }
public string Endpoint { get; } = string.Empty; public EndPoint RemoteEndPoint { get; set; }
public bool IsSecureConnection { get; } = false; public bool IsSecureConnection { get; } = false;
@@ -73,15 +73,8 @@ namespace MQTTnet.Benchmarks
static bool LegacyMethodByStringSplit(string topic, string filter) static bool LegacyMethodByStringSplit(string topic, string filter)
{ {
if (topic == null) ArgumentNullException.ThrowIfNull(topic);
{ ArgumentNullException.ThrowIfNull(filter);
throw new ArgumentNullException(nameof(topic));
}
if (filter == null)
{
throw new ArgumentNullException(nameof(filter));
}
if (string.Equals(topic, filter, StringComparison.Ordinal)) if (string.Equals(topic, filter, StringComparison.Ordinal))
{ {
@@ -10,10 +10,7 @@ namespace MQTTnet.Extensions.Rpc
{ {
public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context) public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context)
{ {
if (context == null) ArgumentNullException.ThrowIfNull(context);
{
throw new ArgumentNullException(nameof(context));
}
if (context.MethodName.Contains("/") || context.MethodName.Contains("+") || context.MethodName.Contains("#")) if (context.MethodName.Contains("/") || context.MethodName.Contains("+") || context.MethodName.Contains("#"))
{ {
@@ -20,15 +20,8 @@ namespace MQTTnet.Extensions.Rpc
public static IMqttRpcClient CreateMqttRpcClient(this MqttClientFactory _, IMqttClient mqttClient, MqttRpcClientOptions rpcClientOptions) public static IMqttRpcClient CreateMqttRpcClient(this MqttClientFactory _, IMqttClient mqttClient, MqttRpcClientOptions rpcClientOptions)
{ {
if (mqttClient == null) ArgumentNullException.ThrowIfNull(mqttClient);
{ ArgumentNullException.ThrowIfNull(rpcClientOptions);
throw new ArgumentNullException(nameof(mqttClient));
}
if (rpcClientOptions == null)
{
throw new ArgumentNullException(nameof(rpcClientOptions));
}
return new MqttRpcClient(mqttClient, rpcClientOptions); return new MqttRpcClient(mqttClient, rpcClientOptions);
} }
@@ -65,10 +65,7 @@ namespace MQTTnet.Extensions.Rpc
public async Task<byte[]> ExecuteAsync(string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, IDictionary<string, object> parameters = null, CancellationToken cancellationToken = default) public async Task<byte[]> ExecuteAsync(string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, IDictionary<string, object> parameters = null, CancellationToken cancellationToken = default)
{ {
if (methodName == null) ArgumentNullException.ThrowIfNull(methodName);
{
throw new ArgumentNullException(nameof(methodName));
}
var context = new TopicGenerationContext(_mqttClient, _options, methodName, parameters, qualityOfServiceLevel); var context = new TopicGenerationContext(_mqttClient, _options, methodName, parameters, qualityOfServiceLevel);
var topicNames = _options.TopicGenerationStrategy.CreateRpcTopics(context); var topicNames = _options.TopicGenerationStrategy.CreateRpcTopics(context);
@@ -41,10 +41,7 @@ public sealed class MqttTopicTemplate : IEquatable<MqttTopicTemplate>
/// </exception> /// </exception>
public MqttTopicTemplate(string topicTemplate) public MqttTopicTemplate(string topicTemplate)
{ {
if (topicTemplate == null) ArgumentNullException.ThrowIfNull(topicTemplate);
{
throw new ArgumentNullException(nameof(topicTemplate));
}
MqttTopicValidator.ThrowIfInvalidSubscribe(topicTemplate); MqttTopicValidator.ThrowIfInvalidSubscribe(topicTemplate);
@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using MQTTnet.Packets;
namespace MQTTnet.Server.EnhancedAuthentication;
public sealed class ExchangeEnhancedAuthenticationOptions
{
public byte[] AuthenticationData { get; set; }
public string ReasonString { get; set; }
public List<MqttUserProperty> UserProperties { get; set; }
}
@@ -0,0 +1,70 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text;
using MQTTnet.Packets;
namespace MQTTnet.Server.EnhancedAuthentication;
public sealed class ExchangeEnhancedAuthenticationOptionsFactory
{
readonly ExchangeEnhancedAuthenticationOptions _options = new();
public ExchangeEnhancedAuthenticationOptions Build()
{
return _options;
}
public ExchangeEnhancedAuthenticationOptionsFactory WithAuthenticationData(byte[] authenticationData)
{
_options.AuthenticationData = authenticationData;
return this;
}
public ExchangeEnhancedAuthenticationOptionsFactory WithAuthenticationData(string authenticationData)
{
if (authenticationData == null)
{
_options.AuthenticationData = null;
}
else
{
_options.AuthenticationData = Encoding.UTF8.GetBytes(authenticationData);
}
return this;
}
public ExchangeEnhancedAuthenticationOptionsFactory WithReasonString(string reasonString)
{
_options.ReasonString = reasonString;
return this;
}
public ExchangeEnhancedAuthenticationOptionsFactory WithUserProperties(List<MqttUserProperty> userProperties)
{
_options.UserProperties = userProperties;
return this;
}
public ExchangeEnhancedAuthenticationOptionsFactory WithUserProperty(string name, string value)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (_options.UserProperties == null)
{
_options.UserProperties = new List<MqttUserProperty>();
}
_options.UserProperties.Add(new MqttUserProperty(name, value));
return this;
}
}
@@ -0,0 +1,12 @@
using MQTTnet.Packets;
namespace MQTTnet.Server.EnhancedAuthentication;
public sealed class ExchangeEnhancedAuthenticationResult
{
public string ReasonString { get; set; }
public List<MqttUserProperty> UserProperties { get; set; }
public byte[] AuthenticationData { get; set; }
}
@@ -0,0 +1,34 @@
using MQTTnet.Packets;
namespace MQTTnet.Server.EnhancedAuthentication;
public static class ExchangeEnhancedAuthenticationResultFactory
{
public static ExchangeEnhancedAuthenticationResult Create(MqttAuthPacket authPacket)
{
ArgumentNullException.ThrowIfNull(authPacket);
return new ExchangeEnhancedAuthenticationResult
{
AuthenticationData = authPacket.AuthenticationData,
ReasonString = authPacket.ReasonString,
UserProperties = authPacket.UserProperties
};
}
public static ExchangeEnhancedAuthenticationResult Create(MqttDisconnectPacket disconnectPacket)
{
ArgumentNullException.ThrowIfNull(disconnectPacket);
return new ExchangeEnhancedAuthenticationResult
{
AuthenticationData = null,
ReasonString = disconnectPacket.ReasonString,
UserProperties = disconnectPacket.UserProperties
// SessionExpiryInterval makes no sense because the connection is not yet made!
// ServerReferences makes no sense when the client initiated a DISCONNECT!
};
}
}
@@ -10,9 +10,10 @@ namespace MQTTnet.Server
{ {
public sealed class ClientAcknowledgedPublishPacketEventArgs : EventArgs public sealed class ClientAcknowledgedPublishPacketEventArgs : EventArgs
{ {
public ClientAcknowledgedPublishPacketEventArgs(string clientId, IDictionary sessionItems, MqttPublishPacket publishPacket, MqttPacketWithIdentifier acknowledgePacket) public ClientAcknowledgedPublishPacketEventArgs(string clientId, string userName, IDictionary sessionItems, MqttPublishPacket publishPacket, MqttPacketWithIdentifier acknowledgePacket)
{ {
ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId)); ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
UserName = userName;
SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems)); SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems));
PublishPacket = publishPacket ?? throw new ArgumentNullException(nameof(publishPacket)); PublishPacket = publishPacket ?? throw new ArgumentNullException(nameof(publishPacket));
AcknowledgePacket = acknowledgePacket ?? throw new ArgumentNullException(nameof(acknowledgePacket)); AcknowledgePacket = acknowledgePacket ?? throw new ArgumentNullException(nameof(acknowledgePacket));
@@ -28,6 +29,11 @@ namespace MQTTnet.Server
/// </summary> /// </summary>
public string ClientId { get; } public string ClientId { get; }
/// <summary>
/// Gets the user name of the client.
/// </summary>
public string UserName { get; }
/// <summary> /// <summary>
/// Gets whether the PUBLISH packet is fully acknowledged. This is the case for PUBACK (QoS 1) and PUBCOMP (QoS 2. /// Gets whether the PUBLISH packet is fully acknowledged. This is the case for PUBACK (QoS 1) and PUBCOMP (QoS 2.
/// </summary> /// </summary>
@@ -5,6 +5,8 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net;
using System.Text;
using MQTTnet.Formatter; using MQTTnet.Formatter;
using MQTTnet.Packets; using MQTTnet.Packets;
@@ -14,11 +16,11 @@ namespace MQTTnet.Server
{ {
readonly MqttConnectPacket _connectPacket; readonly MqttConnectPacket _connectPacket;
public ClientConnectedEventArgs(MqttConnectPacket connectPacket, MqttProtocolVersion protocolVersion, string endpoint, IDictionary sessionItems) public ClientConnectedEventArgs(MqttConnectPacket connectPacket, MqttProtocolVersion protocolVersion, EndPoint remoteEndPoint, IDictionary sessionItems)
{ {
_connectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket)); _connectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket));
ProtocolVersion = protocolVersion; ProtocolVersion = protocolVersion;
Endpoint = endpoint; RemoteEndPoint = remoteEndPoint;
SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems)); SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems));
} }
@@ -35,7 +37,10 @@ namespace MQTTnet.Server
/// <summary> /// <summary>
/// Gets the endpoint of the connected client. /// Gets the endpoint of the connected client.
/// </summary> /// </summary>
public string Endpoint { get; } public EndPoint RemoteEndPoint { get; }
[Obsolete("Use RemoteEndPoint instead.")]
public string Endpoint => RemoteEndPoint?.ToString();
/// <summary> /// <summary>
/// Gets the protocol version which is used by the connected client. /// Gets the protocol version which is used by the connected client.
@@ -52,6 +57,11 @@ namespace MQTTnet.Server
/// </summary> /// </summary>
public string UserName => _connectPacket.Username; public string UserName => _connectPacket.Username;
/// <summary>
/// Gets the password of the connected client.
/// </summary>
public string Password => Encoding.UTF8.GetString(_connectPacket.Password.AsSpan());
/// <summary> /// <summary>
/// Gets the user properties sent by the client. /// Gets the user properties sent by the client.
/// <remarks>MQTT 5.0.0+ feature.</remarks> /// <remarks>MQTT 5.0.0+ feature.</remarks>
@@ -5,6 +5,8 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net;
using System.Text;
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Protocol; using MQTTnet.Protocol;
@@ -12,20 +14,21 @@ namespace MQTTnet.Server
{ {
public sealed class ClientDisconnectedEventArgs : EventArgs public sealed class ClientDisconnectedEventArgs : EventArgs
{ {
readonly MqttConnectPacket _connectPacket;
readonly MqttDisconnectPacket _disconnectPacket; readonly MqttDisconnectPacket _disconnectPacket;
public ClientDisconnectedEventArgs( public ClientDisconnectedEventArgs(
string clientId, MqttConnectPacket connectPacket,
MqttDisconnectPacket disconnectPacket, MqttDisconnectPacket disconnectPacket,
MqttClientDisconnectType disconnectType, MqttClientDisconnectType disconnectType,
string endpoint, EndPoint remoteEndPoint,
IDictionary sessionItems) IDictionary sessionItems)
{ {
ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
DisconnectType = disconnectType; DisconnectType = disconnectType;
Endpoint = endpoint; RemoteEndPoint = remoteEndPoint;
SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems)); SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems));
_connectPacket = connectPacket;
// The DISCONNECT packet can be null in case of a non clean disconnect or session takeover. // The DISCONNECT packet can be null in case of a non clean disconnect or session takeover.
_disconnectPacket = disconnectPacket; _disconnectPacket = disconnectPacket;
} }
@@ -34,11 +37,24 @@ namespace MQTTnet.Server
/// Gets the client identifier. /// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues. /// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary> /// </summary>
public string ClientId { get; } public string ClientId => _connectPacket.ClientId;
/// <summary>
/// Gets the user name of the client.
/// </summary>
public string UserName => _connectPacket.Username;
/// <summary>
/// Gets the password of the client.
/// </summary>
public string Password => Encoding.UTF8.GetString(_connectPacket.Password.AsSpan());
public MqttClientDisconnectType DisconnectType { get; } public MqttClientDisconnectType DisconnectType { get; }
public string Endpoint { get; } public EndPoint RemoteEndPoint { get; }
[Obsolete("Use RemoteEndPoint instead.")]
public string Endpoint => RemoteEndPoint?.ToString();
/// <summary> /// <summary>
/// Gets the reason code sent by the client. /// Gets the reason code sent by the client.
@@ -10,9 +10,10 @@ namespace MQTTnet.Server
{ {
public sealed class ClientSubscribedTopicEventArgs : EventArgs public sealed class ClientSubscribedTopicEventArgs : EventArgs
{ {
public ClientSubscribedTopicEventArgs(string clientId, MqttTopicFilter topicFilter, IDictionary sessionItems) public ClientSubscribedTopicEventArgs(string clientId, string userName, MqttTopicFilter topicFilter, IDictionary sessionItems)
{ {
ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId)); ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
UserName = userName;
TopicFilter = topicFilter ?? throw new ArgumentNullException(nameof(topicFilter)); TopicFilter = topicFilter ?? throw new ArgumentNullException(nameof(topicFilter));
SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems)); SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems));
} }
@@ -23,6 +24,11 @@ namespace MQTTnet.Server
/// </summary> /// </summary>
public string ClientId { get; } public string ClientId { get; }
/// <summary>
/// Gets the user name of the client.
/// </summary>
public string UserName { get; }
/// <summary> /// <summary>
/// Gets or sets a key/value collection that can be used to share data within the scope of this session. /// Gets or sets a key/value collection that can be used to share data within the scope of this session.
/// </summary> /// </summary>
@@ -9,9 +9,10 @@ namespace MQTTnet.Server
{ {
public sealed class ClientUnsubscribedTopicEventArgs : EventArgs public sealed class ClientUnsubscribedTopicEventArgs : EventArgs
{ {
public ClientUnsubscribedTopicEventArgs(string clientId, string topicFilter, IDictionary sessionItems) public ClientUnsubscribedTopicEventArgs(string clientId, string userName, string topicFilter, IDictionary sessionItems)
{ {
ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId)); ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
UserName = userName;
TopicFilter = topicFilter ?? throw new ArgumentNullException(nameof(topicFilter)); TopicFilter = topicFilter ?? throw new ArgumentNullException(nameof(topicFilter));
SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems)); SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems));
} }
@@ -22,6 +23,11 @@ namespace MQTTnet.Server
/// </summary> /// </summary>
public string ClientId { get; } public string ClientId { get; }
/// <summary>
/// Gets the user name of the client.
/// </summary>
public string UserName { get; }
/// <summary> /// <summary>
/// Gets or sets a key/value collection that can be used to share data within the scope of this session. /// Gets or sets a key/value collection that can be used to share data within the scope of this session.
/// </summary> /// </summary>
@@ -4,6 +4,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Net;
using System.Threading; using System.Threading;
using MQTTnet.Packets; using MQTTnet.Packets;
@@ -11,11 +12,12 @@ namespace MQTTnet.Server
{ {
public sealed class InterceptingPacketEventArgs : EventArgs public sealed class InterceptingPacketEventArgs : EventArgs
{ {
public InterceptingPacketEventArgs(CancellationToken cancellationToken, string clientId, string endpoint, MqttPacket packet, IDictionary sessionItems) public InterceptingPacketEventArgs(CancellationToken cancellationToken, string clientId, string userName, EndPoint remoteEndPoint, MqttPacket packet, IDictionary sessionItems)
{ {
CancellationToken = cancellationToken; CancellationToken = cancellationToken;
ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId)); ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
Endpoint = endpoint; UserName = userName;
RemoteEndPoint = remoteEndPoint;
Packet = packet ?? throw new ArgumentNullException(nameof(packet)); Packet = packet ?? throw new ArgumentNullException(nameof(packet));
SessionItems = sessionItems; SessionItems = sessionItems;
} }
@@ -31,10 +33,18 @@ namespace MQTTnet.Server
/// </summary> /// </summary>
public string ClientId { get; } public string ClientId { get; }
/// <summary>
/// Gets the user name of the client.
/// </summary>
public string UserName { get; }
/// <summary> /// <summary>
/// Gets the endpoint of the sending or receiving client. /// Gets the endpoint of the sending or receiving client.
/// </summary> /// </summary>
public string Endpoint { get; } public EndPoint RemoteEndPoint { get; }
[Obsolete("Use RemoteEndPoint instead.")]
public string Endpoint => RemoteEndPoint?.ToString();
/// <summary> /// <summary>
/// Gets or sets the MQTT packet which was received or will be sent. /// Gets or sets the MQTT packet which was received or will be sent.
@@ -10,11 +10,12 @@ namespace MQTTnet.Server
{ {
public sealed class InterceptingPublishEventArgs : EventArgs public sealed class InterceptingPublishEventArgs : EventArgs
{ {
public InterceptingPublishEventArgs(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken, string clientId, IDictionary sessionItems) public InterceptingPublishEventArgs(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken, string clientId, string userName, IDictionary sessionItems)
{ {
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage)); ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
CancellationToken = cancellationToken; CancellationToken = cancellationToken;
ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId)); ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
UserName = userName;
SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems)); SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems));
} }
@@ -31,6 +32,11 @@ namespace MQTTnet.Server
/// </summary> /// </summary>
public string ClientId { get; } public string ClientId { get; }
/// <summary>
/// Gets the user name of the client.
/// </summary>
public string UserName { get; }
public bool CloseConnection { get; set; } public bool CloseConnection { get; set; }
/// <summary> /// <summary>
@@ -15,12 +15,14 @@ namespace MQTTnet.Server
public InterceptingSubscriptionEventArgs( public InterceptingSubscriptionEventArgs(
CancellationToken cancellationToken, CancellationToken cancellationToken,
string clientId, string clientId,
string userName,
MqttSessionStatus session, MqttSessionStatus session,
MqttTopicFilter topicFilter, MqttTopicFilter topicFilter,
List<MqttUserProperty> userProperties) List<MqttUserProperty> userProperties)
{ {
CancellationToken = cancellationToken; CancellationToken = cancellationToken;
ClientId = clientId; ClientId = clientId;
UserName = userName;
Session = session; Session = session;
TopicFilter = topicFilter; TopicFilter = topicFilter;
UserProperties = userProperties; UserProperties = userProperties;
@@ -37,6 +39,11 @@ namespace MQTTnet.Server
/// </summary> /// </summary>
public string ClientId { get; } public string ClientId { get; }
/// <summary>
/// Gets the user name of the client.
/// </summary>
public string UserName { get; }
/// <summary> /// <summary>
/// Gets or sets whether the broker should close the client connection. /// Gets or sets whether the broker should close the client connection.
/// </summary> /// </summary>
@@ -12,10 +12,11 @@ namespace MQTTnet.Server
{ {
public sealed class InterceptingUnsubscriptionEventArgs : EventArgs public sealed class InterceptingUnsubscriptionEventArgs : EventArgs
{ {
public InterceptingUnsubscriptionEventArgs(CancellationToken cancellationToken, string clientId, IDictionary sessionItems, string topic, List<MqttUserProperty> userProperties) public InterceptingUnsubscriptionEventArgs(CancellationToken cancellationToken, string clientId, string userName, IDictionary sessionItems, string topic, List<MqttUserProperty> userProperties)
{ {
CancellationToken = cancellationToken; CancellationToken = cancellationToken;
ClientId = clientId; ClientId = clientId;
UserName = userName;
SessionItems = sessionItems; SessionItems = sessionItems;
Topic = topic; Topic = topic;
UserProperties = userProperties; UserProperties = userProperties;
@@ -32,6 +33,11 @@ namespace MQTTnet.Server
/// </summary> /// </summary>
public string ClientId { get; } public string ClientId { get; }
/// <summary>
/// Gets the user name of the client.
/// </summary>
public string UserName { get; }
/// <summary> /// <summary>
/// Gets or sets whether the broker should close the client connection. /// Gets or sets whether the broker should close the client connection.
/// </summary> /// </summary>
@@ -9,9 +9,10 @@ namespace MQTTnet.Server
{ {
public sealed class SessionDeletedEventArgs : EventArgs public sealed class SessionDeletedEventArgs : EventArgs
{ {
public SessionDeletedEventArgs(string id, IDictionary sessionItems) public SessionDeletedEventArgs(string id, string userName, IDictionary sessionItems)
{ {
Id = id ?? throw new ArgumentNullException(nameof(id)); Id = id ?? throw new ArgumentNullException(nameof(id));
UserName = userName;
SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems)); SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems));
} }
@@ -20,6 +21,11 @@ namespace MQTTnet.Server
/// </summary> /// </summary>
public string Id { get; } public string Id { get; }
/// <summary>
/// Gets the user name of the session.
/// </summary>
public string UserName { get; }
/// <summary> /// <summary>
/// Gets or sets a key/value collection that can be used to share data within the scope of this session. /// Gets or sets a key/value collection that can be used to share data within the scope of this session.
/// </summary> /// </summary>
@@ -2,189 +2,243 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Net;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using MQTTnet.Adapter; using MQTTnet.Adapter;
using MQTTnet.Exceptions;
using MQTTnet.Formatter; using MQTTnet.Formatter;
using MQTTnet.Internal; using MQTTnet.Internal;
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Protocol; using MQTTnet.Protocol;
using MQTTnet.Server.EnhancedAuthentication;
namespace MQTTnet.Server namespace MQTTnet.Server;
public sealed class ValidatingConnectionEventArgs : EventArgs
{ {
public sealed class ValidatingConnectionEventArgs : EventArgs readonly MqttConnectPacket _connectPacket;
{
readonly MqttConnectPacket _connectPacket;
public ValidatingConnectionEventArgs(MqttConnectPacket connectPacket, IMqttChannelAdapter clientAdapter, IDictionary sessionItems) public ValidatingConnectionEventArgs(MqttConnectPacket connectPacket, IMqttChannelAdapter clientAdapter, IDictionary sessionItems, CancellationToken cancellationToken)
{
_connectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket));
ChannelAdapter = clientAdapter ?? throw new ArgumentNullException(nameof(clientAdapter));
SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems));
CancellationToken = cancellationToken;
}
/// <summary>
/// Gets or sets the assigned client identifier.
/// MQTTv5 only.
/// </summary>
public string AssignedClientIdentifier { get; set; }
/// <summary>
/// Gets or sets the authentication data.
/// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary>
public byte[] AuthenticationData => _connectPacket.AuthenticationData;
/// <summary>
/// Gets or sets the authentication method.
/// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary>
public string AuthenticationMethod => _connectPacket.AuthenticationMethod;
public CancellationToken CancellationToken { get; }
/// <summary>
/// Gets the channel adapter. This can be a _MqttConnectionContext_ (used in ASP.NET), a _MqttChannelAdapter_ (used for
/// TCP or WebSockets) or a custom implementation.
/// </summary>
public IMqttChannelAdapter ChannelAdapter { get; }
/// <summary>
/// Gets or sets a value indicating whether clean sessions are used or not.
/// When a client connects to a broker it can connect using either a non persistent connection (clean session) or a
/// persistent connection.
/// With a non persistent connection the broker doesn't store any subscription information or undelivered messages for
/// the client.
/// This mode is ideal when the client only publishes messages.
/// It can also connect as a durable client using a persistent connection.
/// In this mode, the broker will store subscription information, and undelivered messages for the client.
/// </summary>
public bool? CleanSession => _connectPacket.CleanSession;
public X509Certificate2 ClientCertificate => ChannelAdapter.ClientCertificate;
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
public string ClientId => _connectPacket.ClientId;
[Obsolete("Use RemoteEndPoint instead.")]
public string Endpoint => RemoteEndPoint?.ToString();
public bool IsSecureConnection => ChannelAdapter.IsSecureConnection;
/// <summary>
/// Gets or sets the keep alive period.
/// The connection is normally left open by the client so that is can send and receive data at any time.
/// If no data flows over an open connection for a certain time period then the client will generate a PINGREQ and
/// expect to receive a PINGRESP from the broker.
/// This message exchange confirms that the connection is open and working.
/// This period is known as the keep alive period.
/// </summary>
public ushort? KeepAlivePeriod => _connectPacket.KeepAlivePeriod;
/// <summary>
/// A value of 0 indicates that the value is not used.
/// </summary>
public uint MaximumPacketSize => _connectPacket.MaximumPacketSize;
public string Password => Encoding.UTF8.GetString(RawPassword ?? EmptyBuffer.Array);
public MqttProtocolVersion ProtocolVersion => ChannelAdapter.PacketFormatterAdapter.ProtocolVersion;
public byte[] RawPassword => _connectPacket.Password;
/// <summary>
/// Gets or sets the reason code. When a MQTTv3 client connects the enum value must be one which is
/// also supported in MQTTv3. Otherwise the connection attempt will fail because not all codes can be
/// converted properly.
/// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary>
public MqttConnectReasonCode ReasonCode { get; set; } = MqttConnectReasonCode.Success;
public string ReasonString { get; set; }
/// <summary>
/// Gets or sets the receive maximum.
/// This gives the maximum length of the received messages.
/// A value of 0 indicates that the value is not used.
/// </summary>
public ushort ReceiveMaximum => _connectPacket.ReceiveMaximum;
public EndPoint RemoteEndPoint => ChannelAdapter.RemoteEndPoint;
/// <summary>
/// Gets the request problem information.
/// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary>
public bool RequestProblemInformation => _connectPacket.RequestProblemInformation;
/// <summary>
/// Gets the request response information.
/// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary>
public bool RequestResponseInformation => _connectPacket.RequestResponseInformation;
/// <summary>
/// Gets or sets the response authentication data.
/// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary>
public byte[] ResponseAuthenticationData { get; set; }
/// <summary>
/// Gets or sets the response user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT
/// packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add
/// metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary>
public List<MqttUserProperty> ResponseUserProperties { get; set; }
/// <summary>
/// Gets or sets the server reference. This can be used together with i.e. "Server Moved" to send
/// a different server address to the client.
/// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary>
public string ServerReference { get; set; }
/// <summary>
/// Gets the session expiry interval.
/// The time after a session expires when it's not actively used.
/// A value of 0 means no expiation.
/// </summary>
public uint SessionExpiryInterval => _connectPacket.SessionExpiryInterval;
/// <summary>
/// Gets or sets a key/value collection that can be used to share data within the scope of this session.
/// </summary>
public IDictionary SessionItems { get; }
/// <summary>
/// Gets or sets the topic alias maximum.
/// This gives the maximum length of the topic alias.
/// A value of 0 indicates that the value is not used.
/// </summary>
public ushort TopicAliasMaximum => _connectPacket.TopicAliasMaximum;
public string UserName => _connectPacket.Username;
/// <summary>
/// Gets or sets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT
/// packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add
/// metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary>
public List<MqttUserProperty> UserProperties => _connectPacket.UserProperties;
/// <summary>
/// Gets or sets the delay interval for the will message.
/// This is the time between the client disconnect and the time the will message will be sent.
/// A value of 0 indicates that the value is not used.
/// </summary>
public uint WillDelayInterval => _connectPacket.WillDelayInterval;
public async Task<ExchangeEnhancedAuthenticationResult> ExchangeEnhancedAuthenticationAsync(
ExchangeEnhancedAuthenticationOptions options,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(options);
var requestAuthPacket = new MqttAuthPacket
{ {
_connectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket)); // From RFC: If the initial CONNECT packet included an Authentication Method property then all AUTH packets,
ChannelAdapter = clientAdapter ?? throw new ArgumentNullException(nameof(clientAdapter)); // and any successful CONNACK packet MUST include an Authentication Method Property with the same value as in the CONNECT packet [MQTT-4.12.0-5].
SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems)); AuthenticationMethod = AuthenticationMethod,
// The reason code will stay at continue all the time when connecting. The server will respond with the
// CONNACK packet when authentication is done!
ReasonCode = MqttAuthenticateReasonCode.ContinueAuthentication,
AuthenticationData = options.AuthenticationData,
ReasonString = options.ReasonString,
UserProperties = options.UserProperties
};
await ChannelAdapter.SendPacketAsync(requestAuthPacket, cancellationToken).ConfigureAwait(false);
var responsePacket = await ChannelAdapter.ReceivePacketAsync(cancellationToken).ConfigureAwait(false);
if (responsePacket == null)
{
throw new MqttCommunicationException("The client closed the connection.");
} }
/// <summary> if (responsePacket is MqttAuthPacket responseAuthPacket)
/// Gets or sets the assigned client identifier. {
/// MQTTv5 only. if (!string.Equals(AuthenticationMethod, responseAuthPacket.AuthenticationMethod, StringComparison.Ordinal))
/// </summary> {
public string AssignedClientIdentifier { get; set; } throw new MqttProtocolViolationException("The authentication method cannot change while authenticating the client.");
}
/// <summary> return ExchangeEnhancedAuthenticationResultFactory.Create(responseAuthPacket);
/// Gets or sets the authentication data. }
/// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary>
public byte[] AuthenticationData => _connectPacket.AuthenticationData;
/// <summary> if (responsePacket is MqttDisconnectPacket disconnectPacket)
/// Gets or sets the authentication method. {
/// <remarks>MQTT 5.0.0+ feature.</remarks> return ExchangeEnhancedAuthenticationResultFactory.Create(disconnectPacket);
/// </summary> }
public string AuthenticationMethod => _connectPacket.AuthenticationMethod;
/// <summary> throw new MqttProtocolViolationException("Received other packet than AUTH while authenticating.");
/// Gets the channel adapter. This can be a _MqttConnectionContext_ (used in ASP.NET), a _MqttChannelAdapter_ (used for
/// TCP or WebSockets) or a custom implementation.
/// </summary>
public IMqttChannelAdapter ChannelAdapter { get; }
/// <summary>
/// Gets or sets a value indicating whether clean sessions are used or not.
/// When a client connects to a broker it can connect using either a non persistent connection (clean session) or a
/// persistent connection.
/// With a non persistent connection the broker doesn't store any subscription information or undelivered messages for
/// the client.
/// This mode is ideal when the client only publishes messages.
/// It can also connect as a durable client using a persistent connection.
/// In this mode, the broker will store subscription information, and undelivered messages for the client.
/// </summary>
public bool? CleanSession => _connectPacket.CleanSession;
public X509Certificate2 ClientCertificate => ChannelAdapter.ClientCertificate;
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
public string ClientId => _connectPacket.ClientId;
public string Endpoint => ChannelAdapter.Endpoint;
public bool IsSecureConnection => ChannelAdapter.IsSecureConnection;
/// <summary>
/// Gets or sets the keep alive period.
/// The connection is normally left open by the client so that is can send and receive data at any time.
/// If no data flows over an open connection for a certain time period then the client will generate a PINGREQ and
/// expect to receive a PINGRESP from the broker.
/// This message exchange confirms that the connection is open and working.
/// This period is known as the keep alive period.
/// </summary>
public ushort? KeepAlivePeriod => _connectPacket.KeepAlivePeriod;
/// <summary>
/// A value of 0 indicates that the value is not used.
/// </summary>
public uint MaximumPacketSize => _connectPacket.MaximumPacketSize;
public string Password => Encoding.UTF8.GetString(RawPassword ?? EmptyBuffer.Array);
public MqttProtocolVersion ProtocolVersion => ChannelAdapter.PacketFormatterAdapter.ProtocolVersion;
public byte[] RawPassword => _connectPacket.Password;
/// <summary>
/// Gets or sets the reason code. When a MQTTv3 client connects the enum value must be one which is
/// also supported in MQTTv3. Otherwise the connection attempt will fail because not all codes can be
/// converted properly.
/// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary>
public MqttConnectReasonCode ReasonCode { get; set; } = MqttConnectReasonCode.Success;
public string ReasonString { get; set; }
/// <summary>
/// Gets or sets the receive maximum.
/// This gives the maximum length of the receive messages.
/// A value of 0 indicates that the value is not used.
/// </summary>
public ushort ReceiveMaximum => _connectPacket.ReceiveMaximum;
/// <summary>
/// Gets the request problem information.
/// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary>
public bool RequestProblemInformation => _connectPacket.RequestProblemInformation;
/// <summary>
/// Gets the request response information.
/// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary>
public bool RequestResponseInformation => _connectPacket.RequestResponseInformation;
/// <summary>
/// Gets or sets the response authentication data.
/// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary>
public byte[] ResponseAuthenticationData { get; set; }
/// <summary>
/// Gets or sets the response user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT
/// packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add
/// metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary>
public List<MqttUserProperty> ResponseUserProperties { get; set; }
/// <summary>
/// Gets or sets the server reference. This can be used together with i.e. "Server Moved" to send
/// a different server address to the client.
/// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary>
public string ServerReference { get; set; }
/// <summary>
/// Gets the session expiry interval.
/// The time after a session expires when it's not actively used.
/// A value of 0 means no expiation.
/// </summary>
public uint SessionExpiryInterval => _connectPacket.SessionExpiryInterval;
/// <summary>
/// Gets or sets a key/value collection that can be used to share data within the scope of this session.
/// </summary>
public IDictionary SessionItems { get; }
/// <summary>
/// Gets or sets the topic alias maximum.
/// This gives the maximum length of the topic alias.
/// A value of 0 indicates that the value is not used.
/// </summary>
public ushort TopicAliasMaximum => _connectPacket.TopicAliasMaximum;
public string UserName => _connectPacket.Username;
/// <summary>
/// Gets or sets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT
/// packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add
/// metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// <remarks>MQTT 5.0.0+ feature.</remarks>
/// </summary>
public List<MqttUserProperty> UserProperties => _connectPacket.UserProperties;
/// <summary>
/// Gets or sets the will delay interval.
/// This is the time between the client disconnect and the time the will message will be sent.
/// A value of 0 indicates that the value is not used.
/// </summary>
public uint WillDelayInterval => _connectPacket.WillDelayInterval;
} }
} }
@@ -23,4 +23,6 @@ public sealed class InjectedMqttApplicationMessage
public IDictionary CustomSessionItems { get; set; } public IDictionary CustomSessionItems { get; set; }
public string SenderClientId { get; set; } = string.Empty; public string SenderClientId { get; set; } = string.Empty;
public string SenderUserName { get; set; }
} }
@@ -167,11 +167,11 @@ namespace MQTTnet.Server.Internal.Adapter
async Task TryHandleClientConnectionAsync(CrossPlatformSocket clientSocket) async Task TryHandleClientConnectionAsync(CrossPlatformSocket clientSocket)
{ {
Stream stream = null; Stream stream = null;
string remoteEndPoint = null; EndPoint remoteEndPoint = null;
try try
{ {
remoteEndPoint = clientSocket.RemoteEndPoint.ToString(); remoteEndPoint = clientSocket.RemoteEndPoint;
_logger.Verbose("TCP client '{0}' accepted (Local endpoint={1})", remoteEndPoint, _localEndPoint); _logger.Verbose("TCP client '{0}' accepted (Local endpoint={1})", remoteEndPoint, _localEndPoint);
@@ -12,10 +12,7 @@ public static class MqttConnAckPacketFactory
{ {
public static MqttConnAckPacket Create(ValidatingConnectionEventArgs validatingConnectionEventArgs) public static MqttConnAckPacket Create(ValidatingConnectionEventArgs validatingConnectionEventArgs)
{ {
if (validatingConnectionEventArgs == null) ArgumentNullException.ThrowIfNull(validatingConnectionEventArgs);
{
throw new ArgumentNullException(nameof(validatingConnectionEventArgs));
}
var connAckPacket = new MqttConnAckPacket var connAckPacket = new MqttConnAckPacket
{ {
@@ -11,15 +11,8 @@ public static class MqttPubAckPacketFactory
{ {
public static MqttPubAckPacket Create(MqttPublishPacket publishPacket, DispatchApplicationMessageResult dispatchApplicationMessageResult) public static MqttPubAckPacket Create(MqttPublishPacket publishPacket, DispatchApplicationMessageResult dispatchApplicationMessageResult)
{ {
if (publishPacket == null) ArgumentNullException.ThrowIfNull(publishPacket);
{ ArgumentNullException.ThrowIfNull(dispatchApplicationMessageResult);
throw new ArgumentNullException(nameof(publishPacket));
}
if (dispatchApplicationMessageResult == null)
{
throw new ArgumentNullException(nameof(dispatchApplicationMessageResult));
}
var pubAckPacket = new MqttPubAckPacket var pubAckPacket = new MqttPubAckPacket
{ {
@@ -11,10 +11,7 @@ public static class MqttPubCompPacketFactory
{ {
public static MqttPubCompPacket Create(MqttPubRelPacket pubRelPacket, MqttApplicationMessageReceivedReasonCode reasonCode) public static MqttPubCompPacket Create(MqttPubRelPacket pubRelPacket, MqttApplicationMessageReceivedReasonCode reasonCode)
{ {
if (pubRelPacket == null) ArgumentNullException.ThrowIfNull(pubRelPacket);
{
throw new ArgumentNullException(nameof(pubRelPacket));
}
return new MqttPubCompPacket return new MqttPubCompPacket
{ {
@@ -11,10 +11,7 @@ public static class MqttPubRecPacketFactory
{ {
public static MqttPacket Create(MqttPublishPacket publishPacket, DispatchApplicationMessageResult dispatchApplicationMessageResult) public static MqttPacket Create(MqttPublishPacket publishPacket, DispatchApplicationMessageResult dispatchApplicationMessageResult)
{ {
if (publishPacket == null) ArgumentNullException.ThrowIfNull(publishPacket);
{
throw new ArgumentNullException(nameof(publishPacket));
}
var pubRecPacket = new MqttPubRecPacket var pubRecPacket = new MqttPubRecPacket
{ {
@@ -11,10 +11,7 @@ public static class MqttPubRelPacketFactory
{ {
public static MqttPubRelPacket Create(MqttPubRecPacket pubRecPacket, MqttApplicationMessageReceivedReasonCode reasonCode) public static MqttPubRelPacket Create(MqttPubRecPacket pubRecPacket, MqttApplicationMessageReceivedReasonCode reasonCode)
{ {
if (pubRecPacket == null) ArgumentNullException.ThrowIfNull(pubRecPacket);
{
throw new ArgumentNullException(nameof(pubRecPacket));
}
return new MqttPubRelPacket return new MqttPubRelPacket
{ {
@@ -11,10 +11,7 @@ public static class MqttPublishPacketFactory
{ {
public static MqttPublishPacket Create(MqttConnectPacket connectPacket) public static MqttPublishPacket Create(MqttConnectPacket connectPacket)
{ {
if (connectPacket == null) ArgumentNullException.ThrowIfNull(connectPacket);
{
throw new ArgumentNullException(nameof(connectPacket));
}
if (!connectPacket.WillFlag) if (!connectPacket.WillFlag)
{ {
@@ -46,10 +43,7 @@ public static class MqttPublishPacketFactory
public static MqttPublishPacket Create(MqttApplicationMessage applicationMessage) public static MqttPublishPacket Create(MqttApplicationMessage applicationMessage)
{ {
if (applicationMessage == null) ArgumentNullException.ThrowIfNull(applicationMessage);
{
throw new ArgumentNullException(nameof(applicationMessage));
}
// Copy all values to their matching counterparts. // Copy all values to their matching counterparts.
// The not supported values in MQTT 3.1.1 are not serialized (excluded) later. // The not supported values in MQTT 3.1.1 are not serialized (excluded) later.
@@ -75,10 +69,7 @@ public static class MqttPublishPacketFactory
public static MqttPublishPacket Create(MqttRetainedMessageMatch retainedMessage) public static MqttPublishPacket Create(MqttRetainedMessageMatch retainedMessage)
{ {
if (retainedMessage == null) ArgumentNullException.ThrowIfNull(retainedMessage);
{
throw new ArgumentNullException(nameof(retainedMessage));
}
var publishPacket = Create(retainedMessage.ApplicationMessage); var publishPacket = Create(retainedMessage.ApplicationMessage);
publishPacket.QualityOfServiceLevel = retainedMessage.SubscriptionQualityOfServiceLevel; publishPacket.QualityOfServiceLevel = retainedMessage.SubscriptionQualityOfServiceLevel;
@@ -10,15 +10,8 @@ public static class MqttSubAckPacketFactory
{ {
public static MqttSubAckPacket Create(MqttSubscribePacket subscribePacket, SubscribeResult subscribeResult) public static MqttSubAckPacket Create(MqttSubscribePacket subscribePacket, SubscribeResult subscribeResult)
{ {
if (subscribePacket == null) ArgumentNullException.ThrowIfNull(subscribePacket);
{ ArgumentNullException.ThrowIfNull(subscribeResult);
throw new ArgumentNullException(nameof(subscribePacket));
}
if (subscribeResult == null)
{
throw new ArgumentNullException(nameof(subscribeResult));
}
var subAckPacket = new MqttSubAckPacket var subAckPacket = new MqttSubAckPacket
{ {
@@ -10,15 +10,8 @@ namespace MQTTnet.Server.Internal.Formatter
{ {
public static MqttUnsubAckPacket Create(MqttUnsubscribePacket unsubscribePacket, UnsubscribeResult unsubscribeResult) public static MqttUnsubAckPacket Create(MqttUnsubscribePacket unsubscribePacket, UnsubscribeResult unsubscribeResult)
{ {
if (unsubscribePacket == null) ArgumentNullException.ThrowIfNull(unsubscribePacket);
{ ArgumentNullException.ThrowIfNull(unsubscribeResult);
throw new ArgumentNullException(nameof(unsubscribePacket));
}
if (unsubscribeResult == null)
{
throw new ArgumentNullException(nameof(unsubscribeResult));
}
var unsubAckPacket = new MqttUnsubAckPacket var unsubAckPacket = new MqttUnsubAckPacket
{ {
@@ -19,29 +19,22 @@ namespace MQTTnet.Server.Internal;
public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification, IDisposable public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification, IDisposable
{ {
readonly Dictionary<string, MqttConnectedClient> _clients = new(4096); readonly Dictionary<string, MqttConnectedClient> _clients = new(4096);
readonly AsyncLock _createConnectionSyncRoot = new(); readonly AsyncLock _createConnectionSyncRoot = new();
readonly MqttServerEventContainer _eventContainer; readonly MqttServerEventContainer _eventContainer;
readonly MqttNetSourceLogger _logger; readonly MqttNetSourceLogger _logger;
readonly MqttServerOptions _options; readonly MqttServerOptions _options;
readonly MqttRetainedMessagesManager _retainedMessagesManager; readonly MqttRetainedMessagesManager _retainedMessagesManager;
readonly IMqttNetLogger _rootLogger; readonly IMqttNetLogger _rootLogger;
readonly ReaderWriterLockSlim _sessionsManagementLock = new(); readonly ReaderWriterLockSlim _sessionsManagementLock = new();
// The _sessions dictionary contains all session, the _subscriberSessions hash set contains subscriber sessions only. // The _sessions dictionary contains all session, the _subscriberSessions hash set contains subscriber sessions only.
// See the MqttSubscription object for a detailed explanation. // See the MqttSubscription object for a detailed explanation.
readonly MqttSessionsStorage _sessionsStorage = new(); readonly MqttSessionsStorage _sessionsStorage = new();
readonly HashSet<MqttSession> _subscriberSessions = new(); readonly HashSet<MqttSession> _subscriberSessions = [];
public MqttClientSessionsManager(MqttServerOptions options, MqttRetainedMessagesManager retainedMessagesManager, MqttServerEventContainer eventContainer, IMqttNetLogger logger) public MqttClientSessionsManager(MqttServerOptions options, MqttRetainedMessagesManager retainedMessagesManager, MqttServerEventContainer eventContainer, IMqttNetLogger logger)
{ {
if (logger == null) ArgumentNullException.ThrowIfNull(logger);
{
throw new ArgumentNullException(nameof(logger));
}
_logger = logger.WithSource(nameof(MqttClientSessionsManager)); _logger = logger.WithSource(nameof(MqttClientSessionsManager));
_rootLogger = logger; _rootLogger = logger;
@@ -53,10 +46,7 @@ public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification
public async Task CloseAllConnections(MqttServerClientDisconnectOptions options) public async Task CloseAllConnections(MqttServerClientDisconnectOptions options)
{ {
if (options == null) ArgumentNullException.ThrowIfNull(options);
{
throw new ArgumentNullException(nameof(options));
}
List<MqttConnectedClient> connections; List<MqttConnectedClient> connections;
lock (_clients) lock (_clients)
@@ -111,7 +101,7 @@ public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification
{ {
if (_eventContainer.SessionDeletedEvent.HasHandlers && session != null) if (_eventContainer.SessionDeletedEvent.HasHandlers && session != null)
{ {
var eventArgs = new SessionDeletedEventArgs(clientId, session.Items); var eventArgs = new SessionDeletedEventArgs(clientId, session.UserName, session.Items);
await _eventContainer.SessionDeletedEvent.TryInvokeAsync(eventArgs, _logger).ConfigureAwait(false); await _eventContainer.SessionDeletedEvent.TryInvokeAsync(eventArgs, _logger).ConfigureAwait(false);
} }
} }
@@ -127,6 +117,7 @@ public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification
public async Task<DispatchApplicationMessageResult> DispatchApplicationMessage( public async Task<DispatchApplicationMessageResult> DispatchApplicationMessage(
string senderId, string senderId,
string senderUserName,
IDictionary senderSessionItems, IDictionary senderSessionItems,
MqttApplicationMessage applicationMessage, MqttApplicationMessage applicationMessage,
CancellationToken cancellationToken) CancellationToken cancellationToken)
@@ -140,7 +131,7 @@ public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification
// Allow the user to intercept application message... // Allow the user to intercept application message...
if (_eventContainer.InterceptingPublishEvent.HasHandlers) if (_eventContainer.InterceptingPublishEvent.HasHandlers)
{ {
var interceptingPublishEventArgs = new InterceptingPublishEventArgs(applicationMessage, cancellationToken, senderId, senderSessionItems); var interceptingPublishEventArgs = new InterceptingPublishEventArgs(applicationMessage, cancellationToken, senderId, senderUserName, senderSessionItems);
if (string.IsNullOrEmpty(interceptingPublishEventArgs.ApplicationMessage.Topic)) if (string.IsNullOrEmpty(interceptingPublishEventArgs.ApplicationMessage.Topic))
{ {
// This can happen if a topic alias us used but the topic is // This can happen if a topic alias us used but the topic is
@@ -320,6 +311,25 @@ public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification
return Task.FromResult((IList<MqttClientStatus>)result); return Task.FromResult((IList<MqttClientStatus>)result);
} }
public Task<MqttSessionStatus> GetSessionStatus(string id)
{
_sessionsManagementLock.EnterReadLock();
try
{
if (!_sessionsStorage.TryGetSession(id, out var session))
{
throw new InvalidOperationException($"Session with ID '{id}' not found.");
}
var sessionStatus = new MqttSessionStatus(session);
return Task.FromResult(sessionStatus);
}
finally
{
_sessionsManagementLock.ExitReadLock();
}
}
public Task<IList<MqttSessionStatus>> GetSessionsStatus() public Task<IList<MqttSessionStatus>> GetSessionsStatus()
{ {
var result = new List<MqttSessionStatus>(); var result = new List<MqttSessionStatus>();
@@ -354,7 +364,7 @@ public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification
return; return;
} }
var validatingConnectionEventArgs = await ValidateConnection(connectPacket, channelAdapter).ConfigureAwait(false); var validatingConnectionEventArgs = await ValidateConnection(connectPacket, channelAdapter, cancellationToken).ConfigureAwait(false);
var connAckPacket = MqttConnAckPacketFactory.Create(validatingConnectionEventArgs); var connAckPacket = MqttConnAckPacketFactory.Create(validatingConnectionEventArgs);
if (validatingConnectionEventArgs.ReasonCode != MqttConnectReasonCode.Success) if (validatingConnectionEventArgs.ReasonCode != MqttConnectReasonCode.Success)
@@ -371,7 +381,11 @@ public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification
if (_eventContainer.ClientConnectedEvent.HasHandlers) if (_eventContainer.ClientConnectedEvent.HasHandlers)
{ {
var eventArgs = new ClientConnectedEventArgs(connectPacket, channelAdapter.PacketFormatterAdapter.ProtocolVersion, channelAdapter.Endpoint, connectedClient.Session.Items); var eventArgs = new ClientConnectedEventArgs(
connectPacket,
channelAdapter.PacketFormatterAdapter.ProtocolVersion,
channelAdapter.RemoteEndPoint,
connectedClient.Session.Items);
await _eventContainer.ClientConnectedEvent.TryInvokeAsync(eventArgs, _logger).ConfigureAwait(false); await _eventContainer.ClientConnectedEvent.TryInvokeAsync(eventArgs, _logger).ConfigureAwait(false);
} }
@@ -409,12 +423,17 @@ public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification
} }
} }
var endpoint = connectedClient.Endpoint; var endpoint = connectedClient.RemoteEndPoint;
if (connectedClient.Id != null && !connectedClient.IsTakenOver && _eventContainer.ClientDisconnectedEvent.HasHandlers) if (connectedClient.Id != null && !connectedClient.IsTakenOver && _eventContainer.ClientDisconnectedEvent.HasHandlers)
{ {
var disconnectType = connectedClient.DisconnectPacket != null ? MqttClientDisconnectType.Clean : MqttClientDisconnectType.NotClean; var disconnectType = connectedClient.DisconnectPacket != null ? MqttClientDisconnectType.Clean : MqttClientDisconnectType.NotClean;
var eventArgs = new ClientDisconnectedEventArgs(connectedClient.Id, connectedClient.DisconnectPacket, disconnectType, endpoint, connectedClient.Session.Items); var eventArgs = new ClientDisconnectedEventArgs(
connectedClient.ConnectPacket,
connectedClient.DisconnectPacket,
disconnectType,
endpoint,
connectedClient.Session.Items);
await _eventContainer.ClientDisconnectedEvent.InvokeAsync(eventArgs).ConfigureAwait(false); await _eventContainer.ClientDisconnectedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
} }
@@ -481,15 +500,8 @@ public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification
public async Task SubscribeAsync(string clientId, ICollection<MqttTopicFilter> topicFilters) public async Task SubscribeAsync(string clientId, ICollection<MqttTopicFilter> topicFilters)
{ {
if (clientId == null) ArgumentNullException.ThrowIfNull(clientId);
{ ArgumentNullException.ThrowIfNull(topicFilters);
throw new ArgumentNullException(nameof(clientId));
}
if (topicFilters == null)
{
throw new ArgumentNullException(nameof(topicFilters));
}
var fakeSubscribePacket = new MqttSubscribePacket(); var fakeSubscribePacket = new MqttSubscribePacket();
fakeSubscribePacket.TopicFilters.AddRange(topicFilters); fakeSubscribePacket.TopicFilters.AddRange(topicFilters);
@@ -510,15 +522,8 @@ public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification
public Task UnsubscribeAsync(string clientId, ICollection<string> topicFilters) public Task UnsubscribeAsync(string clientId, ICollection<string> topicFilters)
{ {
if (clientId == null) ArgumentNullException.ThrowIfNull(clientId);
{ ArgumentNullException.ThrowIfNull(topicFilters);
throw new ArgumentNullException(nameof(clientId));
}
if (topicFilters == null)
{
throw new ArgumentNullException(nameof(topicFilters));
}
var fakeUnsubscribePacket = new MqttUnsubscribePacket(); var fakeUnsubscribePacket = new MqttUnsubscribePacket();
fakeUnsubscribePacket.TopicFilters.AddRange(topicFilters); fakeUnsubscribePacket.TopicFilters.AddRange(topicFilters);
@@ -611,7 +616,12 @@ public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification
if (_eventContainer.ClientDisconnectedEvent.HasHandlers) if (_eventContainer.ClientDisconnectedEvent.HasHandlers)
{ {
var eventArgs = new ClientDisconnectedEventArgs(oldConnectedClient.Id, null, MqttClientDisconnectType.Takeover, oldConnectedClient.Endpoint, oldConnectedClient.Session.Items); var eventArgs = new ClientDisconnectedEventArgs(
oldConnectedClient.ConnectPacket,
null,
MqttClientDisconnectType.Takeover,
oldConnectedClient.RemoteEndPoint,
oldConnectedClient.Session.Items);
await _eventContainer.ClientDisconnectedEvent.TryInvokeAsync(eventArgs, _logger).ConfigureAwait(false); await _eventContainer.ClientDisconnectedEvent.TryInvokeAsync(eventArgs, _logger).ConfigureAwait(false);
} }
@@ -675,14 +685,14 @@ public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
_logger.Warning("Client '{0}': Connected but did not sent a CONNECT packet.", channelAdapter.Endpoint); _logger.Warning("Client '{0}': Connected but did not sent a CONNECT packet.", channelAdapter.RemoteEndPoint);
} }
catch (MqttCommunicationTimedOutException) catch (MqttCommunicationTimedOutException)
{ {
_logger.Warning("Client '{0}': Connected but did not sent a CONNECT packet.", channelAdapter.Endpoint); _logger.Warning("Client '{0}': Connected but did not sent a CONNECT packet.", channelAdapter.RemoteEndPoint);
} }
_logger.Warning("Client '{0}': First received packet was no 'CONNECT' packet [MQTT-3.1.0-1].", channelAdapter.Endpoint); _logger.Warning("Client '{0}': First received packet was no 'CONNECT' packet [MQTT-3.1.0-1].", channelAdapter.RemoteEndPoint);
return null; return null;
} }
@@ -730,11 +740,11 @@ public sealed class MqttClientSessionsManager : ISubscriptionChangedNotification
} }
} }
async Task<ValidatingConnectionEventArgs> ValidateConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter) async Task<ValidatingConnectionEventArgs> ValidateConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
{ {
// TODO: Load session items from persisted sessions in the future. // TODO: Load session items from persisted sessions in the future.
var sessionItems = new ConcurrentDictionary<object, object>(); var sessionItems = new ConcurrentDictionary<object, object>();
var eventArgs = new ValidatingConnectionEventArgs(connectPacket, channelAdapter, sessionItems); var eventArgs = new ValidatingConnectionEventArgs(connectPacket, channelAdapter, sessionItems, cancellationToken);
await _eventContainer.ValidatingConnectionEvent.InvokeAsync(eventArgs).ConfigureAwait(false); await _eventContainer.ValidatingConnectionEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
// Check the client ID and set a random one if supported. // Check the client ID and set a random one if supported.
@@ -50,10 +50,7 @@ namespace MQTTnet.Server.Internal
public void HandleReceivedPacket(MqttPacket packet) public void HandleReceivedPacket(MqttPacket packet)
{ {
if (packet == null) ArgumentNullException.ThrowIfNull(packet);
{
throw new ArgumentNullException(nameof(packet));
}
// This class is tracking all values from Clients perspective! // This class is tracking all values from Clients perspective!
LastPacketSentTimestamp = DateTime.UtcNow; LastPacketSentTimestamp = DateTime.UtcNow;
@@ -74,10 +71,7 @@ namespace MQTTnet.Server.Internal
public void HandleSentPacket(MqttPacket packet) public void HandleSentPacket(MqttPacket packet)
{ {
if (packet == null) ArgumentNullException.ThrowIfNull(packet);
{
throw new ArgumentNullException(nameof(packet));
}
// This class is tracking all values from Clients perspective! // This class is tracking all values from Clients perspective!
LastPacketReceivedTimestamp = DateTime.UtcNow; LastPacketReceivedTimestamp = DateTime.UtcNow;
@@ -161,10 +161,7 @@ namespace MQTTnet.Server.Internal
public async Task<SubscribeResult> Subscribe(MqttSubscribePacket subscribePacket, CancellationToken cancellationToken) public async Task<SubscribeResult> Subscribe(MqttSubscribePacket subscribePacket, CancellationToken cancellationToken)
{ {
if (subscribePacket == null) ArgumentNullException.ThrowIfNull(subscribePacket);
{
throw new ArgumentNullException(nameof(subscribePacket));
}
var retainedApplicationMessages = await _retainedMessagesManager.GetMessages().ConfigureAwait(false); var retainedApplicationMessages = await _retainedMessagesManager.GetMessages().ConfigureAwait(false);
var result = new SubscribeResult(subscribePacket.TopicFilters.Count); var result = new SubscribeResult(subscribePacket.TopicFilters.Count);
@@ -212,7 +209,7 @@ namespace MQTTnet.Server.Internal
{ {
foreach (var finalTopicFilter in finalTopicFilters) foreach (var finalTopicFilter in finalTopicFilters)
{ {
var eventArgs = new ClientSubscribedTopicEventArgs(_session.Id, finalTopicFilter, _session.Items); var eventArgs = new ClientSubscribedTopicEventArgs(_session.Id, _session.UserName, finalTopicFilter, _session.Items);
await _eventContainer.ClientSubscribedTopicEvent.InvokeAsync(eventArgs).ConfigureAwait(false); await _eventContainer.ClientSubscribedTopicEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
} }
} }
@@ -222,10 +219,7 @@ namespace MQTTnet.Server.Internal
public async Task<UnsubscribeResult> Unsubscribe(MqttUnsubscribePacket unsubscribePacket, CancellationToken cancellationToken) public async Task<UnsubscribeResult> Unsubscribe(MqttUnsubscribePacket unsubscribePacket, CancellationToken cancellationToken)
{ {
if (unsubscribePacket == null) ArgumentNullException.ThrowIfNull(unsubscribePacket);
{
throw new ArgumentNullException(nameof(unsubscribePacket));
}
var result = new UnsubscribeResult(); var result = new UnsubscribeResult();
@@ -303,7 +297,7 @@ namespace MQTTnet.Server.Internal
{ {
foreach (var topicFilter in unsubscribePacket.TopicFilters) foreach (var topicFilter in unsubscribePacket.TopicFilters)
{ {
var eventArgs = new ClientUnsubscribedTopicEventArgs(_session.Id, topicFilter, _session.Items); var eventArgs = new ClientUnsubscribedTopicEventArgs(_session.Id, _session.UserName, topicFilter, _session.Items);
await _eventContainer.ClientUnsubscribedTopicEvent.InvokeAsync(eventArgs).ConfigureAwait(false); await _eventContainer.ClientUnsubscribedTopicEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
} }
} }
@@ -466,7 +460,7 @@ namespace MQTTnet.Server.Internal
List<MqttUserProperty> userProperties, List<MqttUserProperty> userProperties,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var eventArgs = new InterceptingSubscriptionEventArgs(cancellationToken, _session.Id, new MqttSessionStatus(_session), topicFilter, userProperties); var eventArgs = new InterceptingSubscriptionEventArgs(cancellationToken, _session.Id, _session.UserName, new MqttSessionStatus(_session), topicFilter, userProperties);
if (topicFilter.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce) if (topicFilter.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
{ {
@@ -499,7 +493,7 @@ namespace MQTTnet.Server.Internal
List<MqttUserProperty> userProperties, List<MqttUserProperty> userProperties,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var clientUnsubscribingTopicEventArgs = new InterceptingUnsubscriptionEventArgs(cancellationToken, _session.Id, _session.Items, topicFilter, userProperties) var clientUnsubscribingTopicEventArgs = new InterceptingUnsubscriptionEventArgs(cancellationToken, _session.Id, _session.UserName, _session.Items, topicFilter, userProperties)
{ {
Response = Response =
{ {
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System.Net;
using MQTTnet.Adapter; using MQTTnet.Adapter;
using MQTTnet.Diagnostics.Logger; using MQTTnet.Diagnostics.Logger;
using MQTTnet.Exceptions; using MQTTnet.Exceptions;
@@ -45,13 +46,10 @@ public sealed class MqttConnectedClient : IDisposable
ConnectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket)); ConnectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket));
ChannelAdapter = channelAdapter ?? throw new ArgumentNullException(nameof(channelAdapter)); ChannelAdapter = channelAdapter ?? throw new ArgumentNullException(nameof(channelAdapter));
Endpoint = channelAdapter.Endpoint; RemoteEndPoint = channelAdapter.RemoteEndPoint;
Session = session ?? throw new ArgumentNullException(nameof(session)); Session = session ?? throw new ArgumentNullException(nameof(session));
if (logger == null) ArgumentNullException.ThrowIfNull(logger);
{
throw new ArgumentNullException(nameof(logger));
}
_logger = logger.WithSource(nameof(MqttConnectedClient)); _logger = logger.WithSource(nameof(MqttConnectedClient));
} }
@@ -62,18 +60,20 @@ public sealed class MqttConnectedClient : IDisposable
public MqttDisconnectPacket DisconnectPacket { get; private set; } public MqttDisconnectPacket DisconnectPacket { get; private set; }
public string Endpoint { get; }
public string Id => ConnectPacket.ClientId; public string Id => ConnectPacket.ClientId;
public bool IsRunning { get; private set; } public bool IsRunning { get; private set; }
public bool IsTakenOver { get; set; } public bool IsTakenOver { get; set; }
public EndPoint RemoteEndPoint { get; }
public MqttSession Session { get; } public MqttSession Session { get; }
public MqttClientStatistics Statistics { get; } = new(); public MqttClientStatistics Statistics { get; } = new();
public string UserName => ConnectPacket.Username;
public void Dispose() public void Dispose()
{ {
_cancellationToken?.Dispose(); _cancellationToken?.Dispose();
@@ -119,7 +119,7 @@ public sealed class MqttConnectedClient : IDisposable
var willPublishPacket = MqttPublishPacketFactory.Create(Session.LatestConnectPacket); var willPublishPacket = MqttPublishPacketFactory.Create(Session.LatestConnectPacket);
var willApplicationMessage = MqttApplicationMessageFactory.Create(willPublishPacket); var willApplicationMessage = MqttApplicationMessageFactory.Create(willPublishPacket);
_ = _sessionsManager.DispatchApplicationMessage(Id, Session.Items, willApplicationMessage, CancellationToken.None); _ = _sessionsManager.DispatchApplicationMessage(Id, UserName, Session.Items, willApplicationMessage, CancellationToken.None);
Session.WillMessageSent = true; Session.WillMessageSent = true;
_logger.Info("Client '{0}': Published will message", Id); _logger.Info("Client '{0}': Published will message", Id);
@@ -176,7 +176,7 @@ public sealed class MqttConnectedClient : IDisposable
{ {
if (_eventContainer.ClientAcknowledgedPublishPacketEvent.HasHandlers) if (_eventContainer.ClientAcknowledgedPublishPacketEvent.HasHandlers)
{ {
var eventArgs = new ClientAcknowledgedPublishPacketEventArgs(Id, Session.Items, publishPacket, acknowledgePacket); var eventArgs = new ClientAcknowledgedPublishPacketEventArgs(Id, UserName, Session.Items, publishPacket, acknowledgePacket);
return _eventContainer.ClientAcknowledgedPublishPacketEvent.TryInvokeAsync(eventArgs, _logger); return _eventContainer.ClientAcknowledgedPublishPacketEvent.TryInvokeAsync(eventArgs, _logger);
} }
@@ -219,7 +219,8 @@ public sealed class MqttConnectedClient : IDisposable
var applicationMessage = MqttApplicationMessageFactory.Create(publishPacket); var applicationMessage = MqttApplicationMessageFactory.Create(publishPacket);
var dispatchApplicationMessageResult = await _sessionsManager.DispatchApplicationMessage(Id, Session.Items, applicationMessage, cancellationToken).ConfigureAwait(false); var dispatchApplicationMessageResult =
await _sessionsManager.DispatchApplicationMessage(Id, UserName, Session.Items, applicationMessage, cancellationToken).ConfigureAwait(false);
if (dispatchApplicationMessageResult.CloseConnection) if (dispatchApplicationMessageResult.CloseConnection)
{ {
@@ -341,7 +342,7 @@ public sealed class MqttConnectedClient : IDisposable
return packet; return packet;
} }
var interceptingPacketEventArgs = new InterceptingPacketEventArgs(cancellationToken, Id, Endpoint, packet, Session.Items); var interceptingPacketEventArgs = new InterceptingPacketEventArgs(cancellationToken, Id, UserName, RemoteEndPoint, packet, Session.Items);
await _eventContainer.InterceptingOutboundPacketEvent.InvokeAsync(interceptingPacketEventArgs).ConfigureAwait(false); await _eventContainer.InterceptingOutboundPacketEvent.InvokeAsync(interceptingPacketEventArgs).ConfigureAwait(false);
if (!interceptingPacketEventArgs.ProcessPacket || packet == null) if (!interceptingPacketEventArgs.ProcessPacket || packet == null)
@@ -387,7 +388,7 @@ public sealed class MqttConnectedClient : IDisposable
if (_eventContainer.InterceptingInboundPacketEvent.HasHandlers) if (_eventContainer.InterceptingInboundPacketEvent.HasHandlers)
{ {
var interceptingPacketEventArgs = new InterceptingPacketEventArgs(cancellationToken, Id, Endpoint, currentPacket, Session.Items); var interceptingPacketEventArgs = new InterceptingPacketEventArgs(cancellationToken, Id, UserName, RemoteEndPoint, currentPacket, Session.Items);
await _eventContainer.InterceptingInboundPacketEvent.InvokeAsync(interceptingPacketEventArgs).ConfigureAwait(false); await _eventContainer.InterceptingInboundPacketEvent.InvokeAsync(interceptingPacketEventArgs).ConfigureAwait(false);
currentPacket = interceptingPacketEventArgs.Packet; currentPacket = interceptingPacketEventArgs.Packet;
processPacket = interceptingPacketEventArgs.ProcessPacket; processPacket = interceptingPacketEventArgs.ProcessPacket;
@@ -563,10 +564,8 @@ public sealed class MqttConnectedClient : IDisposable
var disconnectPacket = MqttDisconnectPacketFactory.Create(options); var disconnectPacket = MqttDisconnectPacketFactory.Create(options);
using (var timeout = new CancellationTokenSource(_serverOptions.DefaultCommunicationTimeout)) using var timeout = new CancellationTokenSource(_serverOptions.DefaultCommunicationTimeout);
{ await SendPacketAsync(disconnectPacket, timeout.Token).ConfigureAwait(false);
await SendPacketAsync(disconnectPacket, timeout.Token).ConfigureAwait(false);
}
} }
catch (Exception exception) catch (Exception exception)
{ {
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System.Collections.ObjectModel;
using MQTTnet.Diagnostics.Logger; using MQTTnet.Diagnostics.Logger;
using MQTTnet.Internal; using MQTTnet.Internal;
@@ -19,8 +20,7 @@ namespace MQTTnet.Server.Internal
{ {
_eventContainer = eventContainer ?? throw new ArgumentNullException(nameof(eventContainer)); _eventContainer = eventContainer ?? throw new ArgumentNullException(nameof(eventContainer));
if (logger == null) ArgumentNullException.ThrowIfNull(logger);
throw new ArgumentNullException(nameof(logger));
_logger = logger.WithSource(nameof(MqttRetainedMessagesManager)); _logger = logger.WithSource(nameof(MqttRetainedMessagesManager));
} }
@@ -53,10 +53,7 @@ namespace MQTTnet.Server.Internal
public async Task UpdateMessage(string clientId, MqttApplicationMessage applicationMessage) public async Task UpdateMessage(string clientId, MqttApplicationMessage applicationMessage)
{ {
if (applicationMessage == null) ArgumentNullException.ThrowIfNull(applicationMessage);
{
throw new ArgumentNullException(nameof(applicationMessage));
}
try try
{ {
@@ -27,10 +27,7 @@ namespace MQTTnet.Server.Internal
_options = options ?? throw new ArgumentNullException(nameof(options)); _options = options ?? throw new ArgumentNullException(nameof(options));
_sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager)); _sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager));
if (logger == null) ArgumentNullException.ThrowIfNull(logger);
{
throw new ArgumentNullException(nameof(logger));
}
_logger = logger.WithSource(nameof(MqttServerKeepAliveMonitor)); _logger = logger.WithSource(nameof(MqttServerKeepAliveMonitor));
} }
@@ -53,6 +53,8 @@ public sealed class MqttSession : IDisposable
public string Id => _connectPacket.ClientId; public string Id => _connectPacket.ClientId;
public string UserName => _connectPacket.Username;
public IDictionary Items { get; } public IDictionary Items { get; }
public MqttConnectPacket LatestConnectPacket { get; set; } public MqttConnectPacket LatestConnectPacket { get; set; }
@@ -34,10 +34,7 @@ namespace MQTTnet.Server.Internal
public bool TryGetSession(string id, out MqttSession session) public bool TryGetSession(string id, out MqttSession session)
{ {
if (id == null) ArgumentNullException.ThrowIfNull(id);
{
throw new ArgumentNullException(nameof(id));
}
if (!_sessions.TryGetValue(id, out session)) if (!_sessions.TryGetValue(id, out session))
{ {
@@ -63,10 +60,7 @@ namespace MQTTnet.Server.Internal
public bool TryRemoveSession(string id, out MqttSession session) public bool TryRemoveSession(string id, out MqttSession session)
{ {
if (id == null) ArgumentNullException.ThrowIfNull(id);
{
throw new ArgumentNullException(nameof(id));
}
if (_sessions.TryGetValue(id, out session)) if (_sessions.TryGetValue(id, out session))
{ {
@@ -79,10 +73,7 @@ namespace MQTTnet.Server.Internal
public void UpdateSession(string id, MqttSession session) public void UpdateSession(string id, MqttSession session)
{ {
if (id == null) ArgumentNullException.ThrowIfNull(id);
{
throw new ArgumentNullException(nameof(id));
}
_sessions[id] = session; _sessions[id] = session;
} }
+20 -52
View File
@@ -31,10 +31,7 @@ public class MqttServer : Disposable
{ {
_options = options ?? throw new ArgumentNullException(nameof(options)); _options = options ?? throw new ArgumentNullException(nameof(options));
if (adapters == null) ArgumentNullException.ThrowIfNull(adapters);
{
throw new ArgumentNullException(nameof(adapters));
}
_adapters = adapters.ToList(); _adapters = adapters.ToList();
@@ -202,15 +199,8 @@ public class MqttServer : Disposable
public Task DisconnectClientAsync(string id, MqttServerClientDisconnectOptions options) public Task DisconnectClientAsync(string id, MqttServerClientDisconnectOptions options)
{ {
if (id == null) ArgumentNullException.ThrowIfNull(id);
{ ArgumentNullException.ThrowIfNull(options);
throw new ArgumentNullException(nameof(id));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
ThrowIfNotStarted(); ThrowIfNotStarted();
@@ -226,10 +216,7 @@ public class MqttServer : Disposable
public Task<MqttApplicationMessage> GetRetainedMessageAsync(string topic) public Task<MqttApplicationMessage> GetRetainedMessageAsync(string topic)
{ {
if (topic == null) ArgumentNullException.ThrowIfNull(topic);
{
throw new ArgumentNullException(nameof(topic));
}
ThrowIfNotStarted(); ThrowIfNotStarted();
@@ -250,17 +237,17 @@ public class MqttServer : Disposable
return _clientSessionsManager.GetSessionsStatus(); return _clientSessionsManager.GetSessionsStatus();
} }
public Task<MqttSessionStatus> GetSessionAsync(string id)
{
ThrowIfNotStarted();
return _clientSessionsManager.GetSessionStatus(id);
}
public Task InjectApplicationMessage(InjectedMqttApplicationMessage injectedApplicationMessage, CancellationToken cancellationToken = default) public Task InjectApplicationMessage(InjectedMqttApplicationMessage injectedApplicationMessage, CancellationToken cancellationToken = default)
{ {
if (injectedApplicationMessage == null) ArgumentNullException.ThrowIfNull(injectedApplicationMessage);
{ ArgumentNullException.ThrowIfNull(injectedApplicationMessage.ApplicationMessage);
throw new ArgumentNullException(nameof(injectedApplicationMessage));
}
if (injectedApplicationMessage.ApplicationMessage == null)
{
throw new ArgumentNullException(nameof(injectedApplicationMessage.ApplicationMessage));
}
MqttTopicValidator.ThrowIfInvalid(injectedApplicationMessage.ApplicationMessage.Topic); MqttTopicValidator.ThrowIfInvalid(injectedApplicationMessage.ApplicationMessage.Topic);
@@ -275,6 +262,7 @@ public class MqttServer : Disposable
return _clientSessionsManager.DispatchApplicationMessage( return _clientSessionsManager.DispatchApplicationMessage(
injectedApplicationMessage.SenderClientId, injectedApplicationMessage.SenderClientId,
injectedApplicationMessage.SenderUserName,
sessionItems, sessionItems,
injectedApplicationMessage.ApplicationMessage, injectedApplicationMessage.ApplicationMessage,
cancellationToken); cancellationToken);
@@ -306,10 +294,7 @@ public class MqttServer : Disposable
public async Task StopAsync(MqttServerStopOptions options) public async Task StopAsync(MqttServerStopOptions options)
{ {
if (options == null) ArgumentNullException.ThrowIfNull(options);
{
throw new ArgumentNullException(nameof(options));
}
try try
{ {
@@ -343,15 +328,8 @@ public class MqttServer : Disposable
public Task SubscribeAsync(string clientId, ICollection<MqttTopicFilter> topicFilters) public Task SubscribeAsync(string clientId, ICollection<MqttTopicFilter> topicFilters)
{ {
if (clientId == null) ArgumentNullException.ThrowIfNull(clientId);
{ ArgumentNullException.ThrowIfNull(topicFilters);
throw new ArgumentNullException(nameof(clientId));
}
if (topicFilters == null)
{
throw new ArgumentNullException(nameof(topicFilters));
}
foreach (var topicFilter in topicFilters) foreach (var topicFilter in topicFilters)
{ {
@@ -366,15 +344,8 @@ public class MqttServer : Disposable
public Task UnsubscribeAsync(string clientId, ICollection<string> topicFilters) public Task UnsubscribeAsync(string clientId, ICollection<string> topicFilters)
{ {
if (clientId == null) ArgumentNullException.ThrowIfNull(clientId);
{ ArgumentNullException.ThrowIfNull(topicFilters);
throw new ArgumentNullException(nameof(clientId));
}
if (topicFilters == null)
{
throw new ArgumentNullException(nameof(topicFilters));
}
ThrowIfDisposed(); ThrowIfDisposed();
ThrowIfNotStarted(); ThrowIfNotStarted();
@@ -384,10 +355,7 @@ public class MqttServer : Disposable
public Task UpdateRetainedMessageAsync(MqttApplicationMessage retainedMessage) public Task UpdateRetainedMessageAsync(MqttApplicationMessage retainedMessage)
{ {
if (retainedMessage == null) ArgumentNullException.ThrowIfNull(retainedMessage);
{
throw new ArgumentNullException(nameof(retainedMessage));
}
ThrowIfDisposed(); ThrowIfDisposed();
ThrowIfNotStarted(); ThrowIfNotStarted();
+10 -45
View File
@@ -13,10 +13,7 @@ public static class MqttServerExtensions
{ {
public static Task DisconnectClientAsync(this MqttServer server, string id, MqttDisconnectReasonCode reasonCode = MqttDisconnectReasonCode.NormalDisconnection) public static Task DisconnectClientAsync(this MqttServer server, string id, MqttDisconnectReasonCode reasonCode = MqttDisconnectReasonCode.NormalDisconnection)
{ {
if (server == null) ArgumentNullException.ThrowIfNull(server);
{
throw new ArgumentNullException(nameof(server));
}
return server.DisconnectClientAsync(id, new MqttServerClientDisconnectOptions { ReasonCode = reasonCode }); return server.DisconnectClientAsync(id, new MqttServerClientDisconnectOptions { ReasonCode = reasonCode });
} }
@@ -28,15 +25,8 @@ public static class MqttServerExtensions
MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce, MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce,
bool retain = false) bool retain = false)
{ {
if (server == null) ArgumentNullException.ThrowIfNull(server);
{ ArgumentNullException.ThrowIfNull(topic);
throw new ArgumentNullException(nameof(server));
}
if (topic == null)
{
throw new ArgumentNullException(nameof(topic));
}
var payloadBuffer = EmptyBuffer.Array; var payloadBuffer = EmptyBuffer.Array;
if (payload is string stringPayload) if (payload is string stringPayload)
@@ -57,50 +47,25 @@ public static class MqttServerExtensions
public static Task StopAsync(this MqttServer server) public static Task StopAsync(this MqttServer server)
{ {
if (server == null) ArgumentNullException.ThrowIfNull(server);
{
throw new ArgumentNullException(nameof(server));
}
return server.StopAsync(new MqttServerStopOptions()); return server.StopAsync(new MqttServerStopOptions());
} }
public static Task SubscribeAsync(this MqttServer server, string clientId, params MqttTopicFilter[] topicFilters) public static Task SubscribeAsync(this MqttServer server, string clientId, params MqttTopicFilter[] topicFilters)
{ {
if (server == null) ArgumentNullException.ThrowIfNull(server);
{ ArgumentNullException.ThrowIfNull(clientId);
throw new ArgumentNullException(nameof(server)); ArgumentNullException.ThrowIfNull(topicFilters);
}
if (clientId == null)
{
throw new ArgumentNullException(nameof(clientId));
}
if (topicFilters == null)
{
throw new ArgumentNullException(nameof(topicFilters));
}
return server.SubscribeAsync(clientId, topicFilters); return server.SubscribeAsync(clientId, topicFilters);
} }
public static Task SubscribeAsync(this MqttServer server, string clientId, string topic) public static Task SubscribeAsync(this MqttServer server, string clientId, string topic)
{ {
if (server == null) ArgumentNullException.ThrowIfNull(server);
{ ArgumentNullException.ThrowIfNull(clientId);
throw new ArgumentNullException(nameof(server)); ArgumentNullException.ThrowIfNull(topic);
}
if (clientId == null)
{
throw new ArgumentNullException(nameof(clientId));
}
if (topic == null)
{
throw new ArgumentNullException(nameof(topic));
}
var topicFilters = new MqttTopicFilterBuilder().WithTopic(topic).Build(); var topicFilters = new MqttTopicFilterBuilder().WithTopic(topic).Build();
return server.SubscribeAsync(clientId, topicFilters); return server.SubscribeAsync(clientId, topicFilters);
+10 -17
View File
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using MQTTnet.Diagnostics.Logger; using MQTTnet.Diagnostics.Logger;
using MQTTnet.Server.EnhancedAuthentication;
using MQTTnet.Server.Internal.Adapter; using MQTTnet.Server.Internal.Adapter;
namespace MQTTnet.Server; namespace MQTTnet.Server;
@@ -32,6 +33,11 @@ public sealed class MqttServerFactory
return new MqttApplicationMessageBuilder(); return new MqttApplicationMessageBuilder();
} }
public ExchangeEnhancedAuthenticationOptionsFactory CreateExchangeExtendedAuthenticationOptionsBuilder()
{
return new ExchangeEnhancedAuthenticationOptionsFactory();
}
public MqttServer CreateMqttServer(MqttServerOptions options) public MqttServer CreateMqttServer(MqttServerOptions options)
{ {
return CreateMqttServer(options, DefaultLogger); return CreateMqttServer(options, DefaultLogger);
@@ -39,10 +45,7 @@ public sealed class MqttServerFactory
public MqttServer CreateMqttServer(MqttServerOptions options, IMqttNetLogger logger) public MqttServer CreateMqttServer(MqttServerOptions options, IMqttNetLogger logger)
{ {
if (logger == null) ArgumentNullException.ThrowIfNull(logger);
{
throw new ArgumentNullException(nameof(logger));
}
var serverAdapters = DefaultServerAdapters.Select(a => a.Invoke(this)); var serverAdapters = DefaultServerAdapters.Select(a => a.Invoke(this));
return CreateMqttServer(options, serverAdapters, logger); return CreateMqttServer(options, serverAdapters, logger);
@@ -50,25 +53,15 @@ public sealed class MqttServerFactory
public MqttServer CreateMqttServer(MqttServerOptions options, IEnumerable<IMqttServerAdapter> serverAdapters, IMqttNetLogger logger) public MqttServer CreateMqttServer(MqttServerOptions options, IEnumerable<IMqttServerAdapter> serverAdapters, IMqttNetLogger logger)
{ {
if (serverAdapters == null) ArgumentNullException.ThrowIfNull(serverAdapters);
{ ArgumentNullException.ThrowIfNull(logger);
throw new ArgumentNullException(nameof(serverAdapters));
}
if (logger == null)
{
throw new ArgumentNullException(nameof(logger));
}
return new MqttServer(options, serverAdapters, logger); return new MqttServer(options, serverAdapters, logger);
} }
public MqttServer CreateMqttServer(MqttServerOptions options, IEnumerable<IMqttServerAdapter> serverAdapters) public MqttServer CreateMqttServer(MqttServerOptions options, IEnumerable<IMqttServerAdapter> serverAdapters)
{ {
if (serverAdapters == null) ArgumentNullException.ThrowIfNull(serverAdapters);
{
throw new ArgumentNullException(nameof(serverAdapters));
}
return new MqttServer(options, serverAdapters, DefaultLogger); return new MqttServer(options, serverAdapters, DefaultLogger);
} }
@@ -2,38 +2,35 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; namespace MQTTnet.Server;
namespace MQTTnet.Server public sealed class MqttServerOptions
{ {
public sealed class MqttServerOptions public TimeSpan DefaultCommunicationTimeout { get; set; } = TimeSpan.FromSeconds(100);
{
public TimeSpan DefaultCommunicationTimeout { get; set; } = TimeSpan.FromSeconds(100);
public MqttServerTcpEndpointOptions DefaultEndpointOptions { get; } = new MqttServerTcpEndpointOptions(); public MqttServerTcpEndpointOptions DefaultEndpointOptions { get; } = new();
public bool EnablePersistentSessions { get; set; } public bool EnablePersistentSessions { get; set; }
public MqttServerKeepAliveOptions KeepAliveOptions { get; } = new MqttServerKeepAliveOptions(); public MqttServerKeepAliveOptions KeepAliveOptions { get; } = new();
public int MaxPendingMessagesPerClient { get; set; } = 250; public int MaxPendingMessagesPerClient { get; set; } = 1000;
public MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; set; } = MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage; public MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; set; } = MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage;
public MqttServerTlsTcpEndpointOptions TlsEndpointOptions { get; } = new MqttServerTlsTcpEndpointOptions(); public MqttServerTlsTcpEndpointOptions TlsEndpointOptions { get; } = new();
/// <summary> /// <summary>
/// Gets or sets the default and initial size of the packet write buffer. /// Gets or sets the default and initial size of the packet write buffer.
/// It is recommended to set this to a value close to the usual expected packet size * 1.5. /// It is recommended to set this to a value close to the usual expected packet size * 1.5.
/// Do not change this value when no memory issues are experienced. /// Do not change this value when no memory issues are experienced.
/// </summary> /// </summary>
public int WriterBufferSize { get; set; } = 4096; public int WriterBufferSize { get; set; } = 4096;
/// <summary> /// <summary>
/// Gets or sets the maximum size of the buffer writer. The writer will reduce its internal buffer /// Gets or sets the maximum size of the buffer writer. The writer will reduce its internal buffer
/// to this value after serializing a packet. /// to this value after serializing a packet.
/// Do not change this value when no memory issues are experienced. /// Do not change this value when no memory issues are experienced.
/// </summary> /// </summary>
public int WriterBufferSizeMax { get; set; } = 65535; public int WriterBufferSizeMax { get; set; } = 65535;
}
} }
@@ -180,10 +180,7 @@ namespace MQTTnet.Server
public MqttServerOptionsBuilder WithEncryptionCertificate(byte[] value, IMqttServerCertificateCredentials credentials = null) public MqttServerOptionsBuilder WithEncryptionCertificate(byte[] value, IMqttServerCertificateCredentials credentials = null)
{ {
if (value == null) ArgumentNullException.ThrowIfNull(value);
{
throw new ArgumentNullException(nameof(value));
}
_options.TlsEndpointOptions.CertificateProvider = new BlobCertificateProvider(value) _options.TlsEndpointOptions.CertificateProvider = new BlobCertificateProvider(value)
{ {
@@ -195,10 +192,7 @@ namespace MQTTnet.Server
public MqttServerOptionsBuilder WithEncryptionCertificate(X509Certificate2 certificate) public MqttServerOptionsBuilder WithEncryptionCertificate(X509Certificate2 certificate)
{ {
if (certificate == null) ArgumentNullException.ThrowIfNull(certificate);
{
throw new ArgumentNullException(nameof(certificate));
}
_options.TlsEndpointOptions.CertificateProvider = new X509CertificateProvider(certificate); _options.TlsEndpointOptions.CertificateProvider = new X509CertificateProvider(certificate);
return this; return this;
@@ -206,10 +200,7 @@ namespace MQTTnet.Server
public MqttServerOptionsBuilder WithEncryptionCertificate(ICertificateProvider certificateProvider) public MqttServerOptionsBuilder WithEncryptionCertificate(ICertificateProvider certificateProvider)
{ {
if (certificateProvider == null) ArgumentNullException.ThrowIfNull(certificateProvider);
{
throw new ArgumentNullException(nameof(certificateProvider));
}
_options.TlsEndpointOptions.CertificateProvider = certificateProvider; _options.TlsEndpointOptions.CertificateProvider = certificateProvider;
@@ -22,7 +22,11 @@ namespace MQTTnet.Server
public bool CheckCertificateRevocation { get; set; } public bool CheckCertificateRevocation { get; set; }
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12; /// <summary>
/// The default value is SslProtocols.None, which allows the operating system to choose the best protocol to use, and to block protocols that are not secure.
/// </summary>
/// <seealso href="https://learn.microsoft.com/en-us/dotnet/api/system.security.authentication.sslprotocols">SslProtocols</seealso>
public SslProtocols SslProtocol { get; set; } = SslProtocols.None;
public System.Net.Security.CipherSuitesPolicy CipherSuitesPolicy { get; set; } public System.Net.Security.CipherSuitesPolicy CipherSuitesPolicy { get; set; }
} }
@@ -4,6 +4,7 @@
using MQTTnet.Formatter; using MQTTnet.Formatter;
using MQTTnet.Server.Internal; using MQTTnet.Server.Internal;
using System.Net;
namespace MQTTnet.Server; namespace MQTTnet.Server;
@@ -22,7 +23,10 @@ public sealed class MqttClientStatus
public DateTime ConnectedTimestamp => _client.Statistics.ConnectedTimestamp; public DateTime ConnectedTimestamp => _client.Statistics.ConnectedTimestamp;
public string Endpoint => _client.Endpoint; public EndPoint RemoteEndPoint => _client.RemoteEndPoint;
[Obsolete("Use RemoteEndPoint instead.")]
public string Endpoint => RemoteEndPoint?.ToString();
/// <summary> /// <summary>
/// Gets or sets the client identifier. /// Gets or sets the client identifier.
@@ -50,10 +54,7 @@ public sealed class MqttClientStatus
public Task DisconnectAsync(MqttServerClientDisconnectOptions options) public Task DisconnectAsync(MqttServerClientDisconnectOptions options)
{ {
if (options == null) ArgumentNullException.ThrowIfNull(options);
{
throw new ArgumentNullException(nameof(options));
}
return _client.StopAsync(options); return _client.StopAsync(options);
} }
@@ -18,10 +18,7 @@ public static class MqttClientStatusExtensions
public static Task DisconnectAsync(this MqttClientStatus clientStatus) public static Task DisconnectAsync(this MqttClientStatus clientStatus)
{ {
if (clientStatus == null) ArgumentNullException.ThrowIfNull(clientStatus);
{
throw new ArgumentNullException(nameof(clientStatus));
}
return clientStatus.DisconnectAsync(DefaultDisconnectOptions); return clientStatus.DisconnectAsync(DefaultDisconnectOptions);
} }
@@ -42,10 +42,7 @@ public sealed class MqttSessionStatus
public Task DeliverApplicationMessageAsync(MqttApplicationMessage applicationMessage) public Task DeliverApplicationMessageAsync(MqttApplicationMessage applicationMessage)
{ {
if (applicationMessage == null) ArgumentNullException.ThrowIfNull(applicationMessage);
{
throw new ArgumentNullException(nameof(applicationMessage));
}
var packetBusItem = new MqttPacketBusItem(MqttPublishPacketFactory.Create(applicationMessage)); var packetBusItem = new MqttPacketBusItem(MqttPublishPacketFactory.Create(applicationMessage));
_session.EnqueueDataPacket(packetBusItem); _session.EnqueueDataPacket(packetBusItem);
@@ -55,10 +52,7 @@ public sealed class MqttSessionStatus
public Task EnqueueApplicationMessageAsync(MqttApplicationMessage applicationMessage) public Task EnqueueApplicationMessageAsync(MqttApplicationMessage applicationMessage)
{ {
if (applicationMessage == null) ArgumentNullException.ThrowIfNull(applicationMessage);
{
throw new ArgumentNullException(nameof(applicationMessage));
}
_session.EnqueueDataPacket(new MqttPacketBusItem(MqttPublishPacketFactory.Create(applicationMessage))); _session.EnqueueDataPacket(new MqttPacketBusItem(MqttPublishPacketFactory.Create(applicationMessage)));
@@ -18,10 +18,7 @@ namespace MQTTnet.Server
public MqttServerStopOptionsBuilder WithDefaultClientDisconnectOptions(Action<MqttServerClientDisconnectOptionsBuilder> builder) public MqttServerStopOptionsBuilder WithDefaultClientDisconnectOptions(Action<MqttServerClientDisconnectOptionsBuilder> builder)
{ {
if (builder == null) ArgumentNullException.ThrowIfNull(builder);
{
throw new ArgumentNullException(nameof(builder));
}
var optionsBuilder = new MqttServerClientDisconnectOptionsBuilder(); var optionsBuilder = new MqttServerClientDisconnectOptionsBuilder();
builder.Invoke(optionsBuilder); builder.Invoke(optionsBuilder);
@@ -14,7 +14,7 @@ namespace MQTTnet.TestApp
public static void ForwardToConsole(MqttNetEventLogger logger) public static void ForwardToConsole(MqttNetEventLogger logger)
{ {
if (logger == null) throw new ArgumentNullException(nameof(logger)); ArgumentNullException.ThrowIfNull(logger);
logger.LogMessagePublished -= PrintToConsole; logger.LogMessagePublished -= PrintToConsole;
logger.LogMessagePublished += PrintToConsole; logger.LogMessagePublished += PrintToConsole;
+3 -1
View File
@@ -43,7 +43,9 @@ namespace MQTTnet.TestApp
{ {
try try
{ {
var options = new MqttServerOptions(); var options = new MqttServerOptionsBuilder()
.WithDefaultEndpoint()
.Build();
// Extend the timestamp for all messages from clients. // Extend the timestamp for all messages from clients.
// Protect several topics from being subscribed from every client. // Protect several topics from being subscribed from every client.
@@ -3,16 +3,16 @@
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; using System;
using System.Collections.Generic; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Exceptions; using MQTTnet.Exceptions;
using MQTTnet.Formatter; using MQTTnet.Formatter;
using MQTTnet.Internal; using MQTTnet.Internal;
using MQTTnet.Packets;
using MQTTnet.Protocol; using MQTTnet.Protocol;
using MQTTnet.Server; using MQTTnet.Server;
using MQTTnet.Server.EnhancedAuthentication;
namespace MQTTnet.Tests.Clients.MqttClient namespace MQTTnet.Tests.Clients.MqttClient
{ {
@@ -24,10 +24,8 @@ namespace MQTTnet.Tests.Clients.MqttClient
public async Task Connect_To_Invalid_Server_Port_Not_Opened() public async Task Connect_To_Invalid_Server_Port_Not_Opened()
{ {
var client = new MqttClientFactory().CreateMqttClient(); var client = new MqttClientFactory().CreateMqttClient();
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5))) using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
{ await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", 12345).Build(), timeout.Token);
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", 12345).Build(), timeout.Token);
}
} }
[TestMethod] [TestMethod]
@@ -35,10 +33,8 @@ namespace MQTTnet.Tests.Clients.MqttClient
public async Task Connect_To_Invalid_Server_Wrong_IP() public async Task Connect_To_Invalid_Server_Wrong_IP()
{ {
var client = new MqttClientFactory().CreateMqttClient(); var client = new MqttClientFactory().CreateMqttClient();
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(2))) using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(2));
{ await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").Build(), timeout.Token);
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").Build(), timeout.Token);
}
} }
[TestMethod] [TestMethod]
@@ -53,179 +49,229 @@ namespace MQTTnet.Tests.Clients.MqttClient
public async Task ConnectTimeout_Throws_Exception() public async Task ConnectTimeout_Throws_Exception()
{ {
var factory = new MqttClientFactory(); var factory = new MqttClientFactory();
using (var client = factory.CreateMqttClient()) using var client = factory.CreateMqttClient();
var disconnectHandlerCalled = false;
try
{ {
var disconnectHandlerCalled = false; client.DisconnectedAsync += _ =>
try
{ {
client.DisconnectedAsync += args => disconnectHandlerCalled = true;
{ return CompletedTask.Instance;
disconnectHandlerCalled = true; };
return CompletedTask.Instance;
};
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").Build()); await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").Build());
Assert.Fail("Must fail!"); Assert.Fail("Must fail!");
}
catch (Exception exception)
{
Assert.IsNotNull(exception);
Assert.IsInstanceOfType(exception, typeof(MqttCommunicationException));
}
await LongTestDelay(); // disconnected handler is called async
Assert.IsTrue(disconnectHandlerCalled);
} }
catch (Exception exception)
{
Assert.IsNotNull(exception);
Assert.IsInstanceOfType(exception, typeof(MqttCommunicationException));
}
await LongTestDelay(); // disconnected handler is called async
Assert.IsTrue(disconnectHandlerCalled);
} }
[TestMethod] [TestMethod]
public async Task Disconnect_Clean() public async Task Disconnect_Clean()
{ {
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500)) using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
var server = await testEnvironment.StartServer();
ClientDisconnectedEventArgs eventArgs = null;
server.ClientDisconnectedAsync += args =>
{ {
var server = await testEnvironment.StartServer(); eventArgs = args;
return CompletedTask.Instance;
};
ClientDisconnectedEventArgs eventArgs = null; var client = await testEnvironment.ConnectClient();
server.ClientDisconnectedAsync += args =>
{
eventArgs = args;
return CompletedTask.Instance;
};
var client = await testEnvironment.ConnectClient(); var disconnectOptions = testEnvironment.ClientFactory.CreateClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.MessageRateTooHigh).Build();
var disconnectOptions = testEnvironment.ClientFactory.CreateClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.MessageRateTooHigh).Build(); // Perform a clean disconnect.
await client.DisconnectAsync(disconnectOptions);
// Perform a clean disconnect. await LongTestDelay();
await client.DisconnectAsync(disconnectOptions);
await LongTestDelay(); Assert.IsNotNull(eventArgs);
Assert.AreEqual(MqttClientDisconnectType.Clean, eventArgs.DisconnectType);
Assert.IsNotNull(eventArgs);
Assert.AreEqual(MqttClientDisconnectType.Clean, eventArgs.DisconnectType);
}
} }
[TestMethod] [TestMethod]
public async Task Disconnect_Clean_With_Custom_Reason() public async Task Disconnect_Clean_With_Custom_Reason()
{ {
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500)) using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
var server = await testEnvironment.StartServer();
ClientDisconnectedEventArgs eventArgs = null;
server.ClientDisconnectedAsync += args =>
{ {
var server = await testEnvironment.StartServer(); eventArgs = args;
return CompletedTask.Instance;
};
ClientDisconnectedEventArgs eventArgs = null; var client = await testEnvironment.ConnectClient();
server.ClientDisconnectedAsync += args =>
{
eventArgs = args;
return CompletedTask.Instance;
};
var client = await testEnvironment.ConnectClient(); var disconnectOptions = testEnvironment.ClientFactory.CreateClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.MessageRateTooHigh).Build();
var disconnectOptions = testEnvironment.ClientFactory.CreateClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.MessageRateTooHigh).Build(); // Perform a clean disconnect.
await client.DisconnectAsync(disconnectOptions);
// Perform a clean disconnect. await LongTestDelay();
await client.DisconnectAsync(disconnectOptions);
await LongTestDelay(); Assert.IsNotNull(eventArgs);
Assert.AreEqual(MqttDisconnectReasonCode.MessageRateTooHigh, eventArgs.ReasonCode);
Assert.IsNotNull(eventArgs);
Assert.AreEqual(MqttDisconnectReasonCode.MessageRateTooHigh, eventArgs.ReasonCode);
}
} }
[TestMethod] [TestMethod]
public async Task Disconnect_Clean_With_User_Properties() public async Task Disconnect_Clean_With_User_Properties()
{ {
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500)) using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
{ var server = await testEnvironment.StartServer();
var server = await testEnvironment.StartServer();
ClientDisconnectedEventArgs eventArgs = null; ClientDisconnectedEventArgs eventArgs = null;
server.ClientDisconnectedAsync += args => server.ClientDisconnectedAsync += args =>
{
eventArgs = args;
return CompletedTask.Instance;
};
var client = await testEnvironment.ConnectClient();
var disconnectOptions = testEnvironment.ClientFactory.CreateClientDisconnectOptionsBuilder().WithUserProperty("test_name", "test_value").Build();
// Perform a clean disconnect.
await client.DisconnectAsync(disconnectOptions);
await LongTestDelay();
Assert.IsNotNull(eventArgs);
Assert.IsNotNull(eventArgs.UserProperties);
Assert.AreEqual(1, eventArgs.UserProperties.Count);
Assert.AreEqual("test_name", eventArgs.UserProperties[0].Name);
Assert.AreEqual("test_value", eventArgs.UserProperties[0].Value);
}
class TestClientKerberosAuthenticationHandler : IMqttEnhancedAuthenticationHandler
{
public async Task HandleEnhancedAuthenticationAsync(MqttEnhancedAuthenticationEventArgs eventArgs)
{
if (eventArgs.AuthenticationMethod != "GS2-KRB5")
{ {
eventArgs = args; throw new InvalidOperationException("Wrong authentication method");
return CompletedTask.Instance; }
var sendOptions = new SendMqttEnhancedAuthenticationDataOptions
{
Data = "initial context token"u8.ToArray()
}; };
var client = await testEnvironment.ConnectClient(); await eventArgs.SendAsync(sendOptions, eventArgs.CancellationToken);
var disconnectOptions = testEnvironment.ClientFactory.CreateClientDisconnectOptionsBuilder().WithUserProperty("test_name", "test_value").Build(); var response = await eventArgs.ReceiveAsync(eventArgs.CancellationToken);
// Perform a clean disconnect. Assert.AreEqual(Encoding.UTF8.GetString(response.AuthenticationData), "reply context token");
await client.DisconnectAsync(disconnectOptions);
await LongTestDelay(); // No further data is required, but we have to fulfil the exchange.
sendOptions = new SendMqttEnhancedAuthenticationDataOptions
{
Data = []
};
Assert.IsNotNull(eventArgs); await eventArgs.SendAsync(sendOptions, eventArgs.CancellationToken);
Assert.IsNotNull(eventArgs.UserProperties);
Assert.AreEqual(1, eventArgs.UserProperties.Count);
Assert.AreEqual("test_name", eventArgs.UserProperties[0].Name);
Assert.AreEqual("test_value", eventArgs.UserProperties[0].Value);
} }
} }
[TestMethod]
public async Task Use_Enhanced_Authentication()
{
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
var server = await testEnvironment.StartServer();
server.ValidatingConnectionAsync += async args =>
{
if (args.AuthenticationMethod == "GS2-KRB5")
{
var result = await args.ExchangeEnhancedAuthenticationAsync(new ExchangeEnhancedAuthenticationOptions(), args.CancellationToken);
Assert.AreEqual(Encoding.UTF8.GetString(result.AuthenticationData), "initial context token");
var authOptions = testEnvironment.ServerFactory.CreateExchangeExtendedAuthenticationOptionsBuilder().WithAuthenticationData("reply context token").Build();
result = await args.ExchangeEnhancedAuthenticationAsync(authOptions, args.CancellationToken);
Assert.AreEqual(Encoding.UTF8.GetString(result.AuthenticationData), "");
args.ResponseAuthenticationData = "outcome of authentication"u8.ToArray();
}
else
{
args.ReasonCode = MqttConnectReasonCode.BadAuthenticationMethod;
}
};
// Use Kerberos sample from the MQTT RFC.
var kerberosAuthenticationHandler = new TestClientKerberosAuthenticationHandler();
var clientOptions = testEnvironment.CreateDefaultClientOptionsBuilder().WithEnhancedAuthentication("GS2-KRB5").WithEnhancedAuthenticationHandler(kerberosAuthenticationHandler);
var client = await testEnvironment.ConnectClient(clientOptions);
Assert.IsTrue(client.IsConnected);
}
[TestMethod] [TestMethod]
public async Task No_Unobserved_Exception() public async Task No_Unobserved_Exception()
{ {
using (var testEnvironment = CreateTestEnvironment()) using var testEnvironment = CreateTestEnvironment();
testEnvironment.IgnoreClientLogErrors = true;
var client = testEnvironment.CreateClient();
var options = new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").WithTimeout(TimeSpan.FromSeconds(2)).Build();
try
{ {
testEnvironment.IgnoreClientLogErrors = true; using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(0.5));
await client.ConnectAsync(options, timeout.Token);
var client = testEnvironment.CreateClient();
var options = new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").WithTimeout(TimeSpan.FromSeconds(2)).Build();
try
{
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(0.5)))
{
await client.ConnectAsync(options, timeout.Token);
}
}
catch (OperationCanceledException)
{
}
client.Dispose();
// These delays and GC calls are required in order to make calling the finalizer reproducible.
GC.Collect();
GC.WaitForPendingFinalizers();
await LongTestDelay();
await LongTestDelay();
await LongTestDelay();
} }
catch (OperationCanceledException)
{
}
client.Dispose();
// These delays and GC calls are required in order to make calling the finalizer reproducible.
GC.Collect();
GC.WaitForPendingFinalizers();
await LongTestDelay();
await LongTestDelay();
await LongTestDelay();
} }
[TestMethod] [TestMethod]
public async Task Return_Non_Success() public async Task Return_Non_Success()
{ {
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500)) using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
var server = await testEnvironment.StartServer();
server.ValidatingConnectionAsync += args =>
{ {
var server = await testEnvironment.StartServer(); args.ResponseUserProperties = [new("Property", "Value")];
server.ValidatingConnectionAsync += args => args.ReasonCode = MqttConnectReasonCode.QuotaExceeded;
{
args.ResponseUserProperties = new List<MqttUserProperty>
{
new MqttUserProperty("Property", "Value")
};
args.ReasonCode = MqttConnectReasonCode.QuotaExceeded; return CompletedTask.Instance;
};
return CompletedTask.Instance; var client = testEnvironment.CreateClient();
};
var client = testEnvironment.CreateClient(); var response = await client.ConnectAsync(testEnvironment.CreateDefaultClientOptionsBuilder().Build());
var response = await client.ConnectAsync(testEnvironment.CreateDefaultClientOptionsBuilder().Build()); Assert.IsNotNull(response);
Assert.AreEqual(MqttClientConnectResultCode.QuotaExceeded, response.ResultCode);
Assert.IsNotNull(response); Assert.AreEqual(response.UserProperties[0].Name, "Property");
Assert.AreEqual(MqttClientConnectResultCode.QuotaExceeded, response.ResultCode); Assert.AreEqual(response.UserProperties[0].Value, "Value");
Assert.AreEqual(response.UserProperties[0].Name, "Property");
Assert.AreEqual(response.UserProperties[0].Value, "Value");
}
} }
[TestMethod] [TestMethod]
@@ -234,10 +280,8 @@ namespace MQTTnet.Tests.Clients.MqttClient
try try
{ {
var mqttFactory = new MqttClientFactory(); var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{ await mqttClient.SubscribeAsync("test", MqttQualityOfServiceLevel.AtLeastOnce);
await mqttClient.SubscribeAsync("test", MqttQualityOfServiceLevel.AtLeastOnce);
}
} }
catch (MqttClientNotConnectedException exception) catch (MqttClientNotConnectedException exception)
{ {
@@ -27,7 +27,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
[TestClass] [TestClass]
public sealed class MqttClient_Tests : BaseTestClass public sealed class MqttClient_Tests : BaseTestClass
{ {
[DataTestMethod] [TestMethod]
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)] [DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
[DataRow(MqttQualityOfServiceLevel.AtMostOnce)] [DataRow(MqttQualityOfServiceLevel.AtMostOnce)]
[DataRow(MqttQualityOfServiceLevel.AtLeastOnce)] [DataRow(MqttQualityOfServiceLevel.AtLeastOnce)]
@@ -11,10 +11,7 @@ namespace MQTTnet.Tests.Helpers
{ {
public static TestApplicationMessageReceivedHandler TrackReceivedMessages(this IMqttClient client) public static TestApplicationMessageReceivedHandler TrackReceivedMessages(this IMqttClient client)
{ {
if (client == null) ArgumentNullException.ThrowIfNull(client);
{
throw new ArgumentNullException(nameof(client));
}
return new TestApplicationMessageReceivedHandler(client); return new TestApplicationMessageReceivedHandler(client);
} }
@@ -11,10 +11,7 @@ namespace MQTTnet.Tests.Helpers
{ {
public static object GetFieldValue(this object source, string fieldName) public static object GetFieldValue(this object source, string fieldName)
{ {
if (source == null) ArgumentNullException.ThrowIfNull(source);
{
throw new ArgumentNullException(nameof(source));
}
var field = source.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); var field = source.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
if (field == null) if (field == null)
+4 -3
View File
@@ -16,9 +16,10 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" /> <PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
<PackageReference Include="MSTest.TestAdapter" Version="3.3.1" /> <PackageReference Include="MSTest.TestAdapter" Version="3.6.3" />
<PackageReference Include="MSTest.TestFramework" Version="3.3.1" /> <PackageReference Include="MSTest.TestFramework" Version="3.6.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<FrameworkReference Include="Microsoft.AspNetCore.App" /> <FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup> </ItemGroup>
@@ -2,10 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using Microsoft.AspNetCore.Http;
using MQTTnet.Channel; using MQTTnet.Channel;
using MQTTnet.Internal; using MQTTnet.Internal;
using System.Buffers; using System.Buffers;
using System.IO; using System.IO;
using System.Net;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -26,7 +28,7 @@ namespace MQTTnet.Tests.Mockups
_stream = new MemoryStream(buffer); _stream = new MemoryStream(buffer);
} }
public string Endpoint { get; } = "<Test channel>"; public EndPoint RemoteEndPoint { get; set; }
public bool IsSecureConnection { get; } = false; public bool IsSecureConnection { get; } = false;
@@ -18,10 +18,7 @@ namespace MQTTnet.Tests.Mockups
public TestApplicationMessageReceivedHandler(IMqttClient mqttClient) public TestApplicationMessageReceivedHandler(IMqttClient mqttClient)
{ {
if (mqttClient == null) ArgumentNullException.ThrowIfNull(mqttClient);
{
throw new ArgumentNullException(nameof(mqttClient));
}
mqttClient.ApplicationMessageReceivedAsync += OnApplicationMessageReceivedAsync; mqttClient.ApplicationMessageReceivedAsync += OnApplicationMessageReceivedAsync;
} }
@@ -102,10 +102,7 @@ namespace MQTTnet.Tests.Mockups
public async Task<IMqttClient> ConnectClient(Action<MqttClientOptionsBuilder> configureOptions, TimeSpan timeout = default) public async Task<IMqttClient> ConnectClient(Action<MqttClientOptionsBuilder> configureOptions, TimeSpan timeout = default)
{ {
if (configureOptions == null) ArgumentNullException.ThrowIfNull(configureOptions);
{
throw new ArgumentNullException(nameof(configureOptions));
}
// Start with initial default values. // Start with initial default values.
var optionsBuilder = ClientFactory.CreateClientOptionsBuilder().WithProtocolVersion(_protocolVersion).WithTcpServer("127.0.0.1", ServerPort); var optionsBuilder = ClientFactory.CreateClientOptionsBuilder().WithProtocolVersion(_protocolVersion).WithTcpServer("127.0.0.1", ServerPort);
@@ -134,10 +131,7 @@ namespace MQTTnet.Tests.Mockups
public async Task<IMqttClient> ConnectClient(MqttClientOptionsBuilder options, TimeSpan timeout = default) public async Task<IMqttClient> ConnectClient(MqttClientOptionsBuilder options, TimeSpan timeout = default)
{ {
if (options == null) ArgumentNullException.ThrowIfNull(options);
{
throw new ArgumentNullException(nameof(options));
}
options = options.WithTcpServer("127.0.0.1", ServerPort); options = options.WithTcpServer("127.0.0.1", ServerPort);
@@ -160,10 +154,7 @@ namespace MQTTnet.Tests.Mockups
public async Task<IMqttClient> ConnectClient(MqttClientOptions options, TimeSpan timeout = default) public async Task<IMqttClient> ConnectClient(MqttClientOptions options, TimeSpan timeout = default)
{ {
if (options == null) ArgumentNullException.ThrowIfNull(options);
{
throw new ArgumentNullException(nameof(options));
}
var client = CreateClient(); var client = CreateClient();
+3 -2
View File
@@ -40,10 +40,11 @@ public class MqttTcpChannel_Tests
}, },
ct.Token); ct.Token);
var remoteEndPoint = new DnsEndPoint("localhost", 50001);
using var clientSocket = new CrossPlatformSocket(AddressFamily.InterNetwork, ProtocolType.Tcp); using var clientSocket = new CrossPlatformSocket(AddressFamily.InterNetwork, ProtocolType.Tcp);
await clientSocket.ConnectAsync(new DnsEndPoint("localhost", 50001), CancellationToken.None); await clientSocket.ConnectAsync(remoteEndPoint, CancellationToken.None);
var tcpChannel = new MqttTcpChannel(clientSocket.GetStream(), "test", null); var tcpChannel = new MqttTcpChannel(clientSocket.GetStream(), remoteEndPoint, null);
await Task.Delay(100, ct.Token); await Task.Delay(100, ct.Token);
+14 -7
View File
@@ -29,16 +29,17 @@ namespace MQTTnet.Tests.Server
return CompletedTask.Instance; return CompletedTask.Instance;
}; };
await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser")); await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser", "ThePassword"));
await LongTestDelay(); await LongTestDelay();
Assert.IsNotNull(eventArgs); Assert.IsNotNull(eventArgs);
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Connected_Event))); Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Connected_Event)));
Assert.IsTrue(eventArgs.Endpoint.Contains("127.0.0.1")); Assert.IsTrue(eventArgs.RemoteEndPoint.ToString().Contains("127.0.0.1"));
Assert.AreEqual(MqttProtocolVersion.V311, eventArgs.ProtocolVersion); Assert.AreEqual(MqttProtocolVersion.V311, eventArgs.ProtocolVersion);
Assert.AreEqual("TheUser", eventArgs.UserName); Assert.AreEqual("TheUser", eventArgs.UserName);
Assert.AreEqual("ThePassword", eventArgs.Password);
} }
} }
@@ -56,7 +57,7 @@ namespace MQTTnet.Tests.Server
return CompletedTask.Instance; return CompletedTask.Instance;
}; };
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser")); var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser", "ThePassword"));
await client.DisconnectAsync(); await client.DisconnectAsync();
await LongTestDelay(); await LongTestDelay();
@@ -64,8 +65,11 @@ namespace MQTTnet.Tests.Server
Assert.IsNotNull(eventArgs); Assert.IsNotNull(eventArgs);
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Disconnected_Event))); Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Disconnected_Event)));
Assert.IsTrue(eventArgs.Endpoint.Contains("127.0.0.1")); Assert.IsTrue(eventArgs.RemoteEndPoint.ToString().Contains("127.0.0.1"));
Assert.AreEqual(MqttClientDisconnectType.Clean, eventArgs.DisconnectType); Assert.AreEqual(MqttClientDisconnectType.Clean, eventArgs.DisconnectType);
Assert.AreEqual("TheUser", eventArgs.UserName);
Assert.AreEqual("ThePassword", eventArgs.Password);
} }
} }
@@ -83,7 +87,7 @@ namespace MQTTnet.Tests.Server
return CompletedTask.Instance; return CompletedTask.Instance;
}; };
var client = await testEnvironment.ConnectClient(); var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
await client.SubscribeAsync("The/Topic", MqttQualityOfServiceLevel.AtLeastOnce); await client.SubscribeAsync("The/Topic", MqttQualityOfServiceLevel.AtLeastOnce);
await LongTestDelay(); await LongTestDelay();
@@ -93,6 +97,7 @@ namespace MQTTnet.Tests.Server
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Subscribed_Event))); Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Subscribed_Event)));
Assert.AreEqual("The/Topic", eventArgs.TopicFilter.Topic); Assert.AreEqual("The/Topic", eventArgs.TopicFilter.Topic);
Assert.AreEqual(MqttQualityOfServiceLevel.AtLeastOnce, eventArgs.TopicFilter.QualityOfServiceLevel); Assert.AreEqual(MqttQualityOfServiceLevel.AtLeastOnce, eventArgs.TopicFilter.QualityOfServiceLevel);
Assert.AreEqual("TheUser", eventArgs.UserName);
} }
} }
@@ -110,7 +115,7 @@ namespace MQTTnet.Tests.Server
return CompletedTask.Instance; return CompletedTask.Instance;
}; };
var client = await testEnvironment.ConnectClient(); var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
await client.UnsubscribeAsync("The/Topic"); await client.UnsubscribeAsync("The/Topic");
await LongTestDelay(); await LongTestDelay();
@@ -119,6 +124,7 @@ namespace MQTTnet.Tests.Server
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Unsubscribed_Event))); Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Unsubscribed_Event)));
Assert.AreEqual("The/Topic", eventArgs.TopicFilter); Assert.AreEqual("The/Topic", eventArgs.TopicFilter);
Assert.AreEqual("TheUser", eventArgs.UserName);
} }
} }
@@ -136,7 +142,7 @@ namespace MQTTnet.Tests.Server
return CompletedTask.Instance; return CompletedTask.Instance;
}; };
var client = await testEnvironment.ConnectClient(); var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
await client.PublishStringAsync("The_Topic", "The_Payload"); await client.PublishStringAsync("The_Topic", "The_Payload");
await LongTestDelay(); await LongTestDelay();
@@ -146,6 +152,7 @@ namespace MQTTnet.Tests.Server
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Application_Message_Received_Event))); Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Application_Message_Received_Event)));
Assert.AreEqual("The_Topic", eventArgs.ApplicationMessage.Topic); Assert.AreEqual("The_Topic", eventArgs.ApplicationMessage.Topic);
Assert.AreEqual("The_Payload", eventArgs.ApplicationMessage.ConvertPayloadToString()); Assert.AreEqual("The_Payload", eventArgs.ApplicationMessage.ConvertPayloadToString());
Assert.AreEqual("TheUser", eventArgs.UserName);
} }
} }
+1 -1
View File
@@ -182,7 +182,7 @@ namespace MQTTnet.Tests.Server
} }
} }
[DataTestMethod] [TestMethod]
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)] [DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
[DataRow(MqttQualityOfServiceLevel.AtLeastOnce)] [DataRow(MqttQualityOfServiceLevel.AtLeastOnce)]
public async Task Retry_If_Not_PubAck(MqttQualityOfServiceLevel qos) public async Task Retry_If_Not_PubAck(MqttQualityOfServiceLevel qos)
@@ -20,7 +20,7 @@ namespace MQTTnet.Tests.Server
[TestClass] [TestClass]
public sealed class Subscribe_Tests : BaseTestClass public sealed class Subscribe_Tests : BaseTestClass
{ {
[DataTestMethod] [TestMethod]
[DataRow("A", "A", true)] [DataRow("A", "A", true)]
[DataRow("A", "B", false)] [DataRow("A", "B", false)]
[DataRow("A", "#", true)] [DataRow("A", "#", true)]
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; using System;
using System.Net;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -18,7 +19,8 @@ public interface IMqttChannelAdapter : IDisposable
long BytesSent { get; } long BytesSent { get; }
X509Certificate2 ClientCertificate { get; } X509Certificate2 ClientCertificate { get; }
string Endpoint { get; }
EndPoint RemoteEndPoint { get; }
bool IsSecureConnection { get; } bool IsSecureConnection { get; }
+3 -5
View File
@@ -5,6 +5,7 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.IO; using System.IO;
using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
@@ -38,10 +39,7 @@ public sealed class MqttChannelAdapter : Disposable, IMqttChannelAdapter
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter)); PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
if (logger == null) ArgumentNullException.ThrowIfNull(logger);
{
throw new ArgumentNullException(nameof(logger));
}
_logger = logger.WithSource(nameof(MqttChannelAdapter)); _logger = logger.WithSource(nameof(MqttChannelAdapter));
} }
@@ -54,7 +52,7 @@ public sealed class MqttChannelAdapter : Disposable, IMqttChannelAdapter
public X509Certificate2 ClientCertificate => _channel.ClientCertificate; public X509Certificate2 ClientCertificate => _channel.ClientCertificate;
public string Endpoint => _channel.Endpoint; public EndPoint RemoteEndPoint => _channel.RemoteEndPoint;
public bool IsSecureConnection => _channel.IsSecureConnection; public bool IsSecureConnection => _channel.IsSecureConnection;
@@ -23,10 +23,7 @@ public sealed class MqttPacketInspector
{ {
_asyncEvent = asyncEvent ?? throw new ArgumentNullException(nameof(asyncEvent)); _asyncEvent = asyncEvent ?? throw new ArgumentNullException(nameof(asyncEvent));
if (logger == null) ArgumentNullException.ThrowIfNull(logger);
{
throw new ArgumentNullException(nameof(logger));
}
_logger = logger.WithSource(nameof(MqttPacketInspector)); _logger = logger.WithSource(nameof(MqttPacketInspector));
} }
+2 -1
View File
@@ -4,6 +4,7 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.Net;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -13,7 +14,7 @@ namespace MQTTnet.Channel;
public interface IMqttChannel : IDisposable public interface IMqttChannel : IDisposable
{ {
X509Certificate2 ClientCertificate { get; } X509Certificate2 ClientCertificate { get; }
string Endpoint { get; } EndPoint RemoteEndPoint { get; }
bool IsSecureConnection { get; } bool IsSecureConnection { get; }
@@ -14,10 +14,7 @@ public sealed class MqttClientConnectResultFactory
{ {
public MqttClientConnectResult Create(MqttConnAckPacket connAckPacket, MqttProtocolVersion protocolVersion) public MqttClientConnectResult Create(MqttConnAckPacket connAckPacket, MqttProtocolVersion protocolVersion)
{ {
if (connAckPacket == null) ArgumentNullException.ThrowIfNull(connAckPacket);
{
throw new ArgumentNullException(nameof(connAckPacket));
}
if (protocolVersion == MqttProtocolVersion.V500) if (protocolVersion == MqttProtocolVersion.V500)
{ {
@@ -68,10 +65,8 @@ public sealed class MqttClientConnectResultFactory
static MqttClientConnectResult CreateForMqtt311(MqttConnAckPacket connAckPacket) static MqttClientConnectResult CreateForMqtt311(MqttConnAckPacket connAckPacket)
{ {
if (connAckPacket == null) ArgumentNullException.ThrowIfNull(connAckPacket);
{
throw new ArgumentNullException(nameof(connAckPacket));
}
return new MqttClientConnectResult return new MqttClientConnectResult
{ {
@@ -84,10 +79,7 @@ public sealed class MqttClientConnectResultFactory
static MqttClientConnectResult CreateForMqtt500(MqttConnAckPacket connAckPacket) static MqttClientConnectResult CreateForMqtt500(MqttConnAckPacket connAckPacket)
{ {
if (connAckPacket == null) ArgumentNullException.ThrowIfNull(connAckPacket);
{
throw new ArgumentNullException(nameof(connAckPacket));
}
return new MqttClientConnectResult return new MqttClientConnectResult
{ {
@@ -209,10 +209,7 @@ namespace MQTTnet.Diagnostics.Logger
public static MqttNetSourceLogger WithSource(this IMqttNetLogger logger, string source) public static MqttNetSourceLogger WithSource(this IMqttNetLogger logger, string source)
{ {
if (logger == null) ArgumentNullException.ThrowIfNull(logger);
{
throw new ArgumentNullException(nameof(logger));
}
return new MqttNetSourceLogger(logger, source); return new MqttNetSourceLogger(logger, source);
} }
@@ -12,10 +12,7 @@ public static class MqttClientDisconnectOptionsValidator
{ {
public static void ThrowIfNotSupported(MqttClientDisconnectOptions options, MqttProtocolVersion protocolVersion) public static void ThrowIfNotSupported(MqttClientDisconnectOptions options, MqttProtocolVersion protocolVersion)
{ {
if (options == null) ArgumentNullException.ThrowIfNull(options);
{
throw new ArgumentNullException(nameof(options));
}
if (protocolVersion == MqttProtocolVersion.V500) if (protocolVersion == MqttProtocolVersion.V500)
{ {
@@ -1,66 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet;
public class MqttExtendedAuthenticationExchangeContext
{
public MqttExtendedAuthenticationExchangeContext(MqttAuthPacket authPacket, MqttClient client)
{
if (authPacket == null)
{
throw new ArgumentNullException(nameof(authPacket));
}
ReasonCode = authPacket.ReasonCode;
ReasonString = authPacket.ReasonString;
AuthenticationMethod = authPacket.AuthenticationMethod;
AuthenticationData = authPacket.AuthenticationData;
UserProperties = authPacket.UserProperties;
Client = client ?? throw new ArgumentNullException(nameof(client));
}
/// <summary>
/// Gets the authentication data.
/// Hint: MQTT 5 feature only.
/// </summary>
public byte[] AuthenticationData { get; }
/// <summary>
/// Gets the authentication method.
/// Hint: MQTT 5 feature only.
/// </summary>
public string AuthenticationMethod { get; }
public MqttClient Client { get; }
/// <summary>
/// Gets the reason code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttAuthenticateReasonCode ReasonCode { get; }
/// <summary>
/// Gets the reason string.
/// Hint: MQTT 5 feature only.
/// </summary>
public string ReasonString { get; }
/// <summary>
/// Gets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT
/// packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add
/// metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
public List<MqttUserProperty> UserProperties { get; }
}
@@ -11,10 +11,7 @@ namespace MQTTnet.Formatter
{ {
public static MqttApplicationMessage Create(MqttPublishPacket publishPacket) public static MqttApplicationMessage Create(MqttPublishPacket publishPacket)
{ {
if (publishPacket == null) ArgumentNullException.ThrowIfNull(publishPacket);
{
throw new ArgumentNullException(nameof(publishPacket));
}
return new MqttApplicationMessage return new MqttApplicationMessage
{ {
+2 -8
View File
@@ -105,10 +105,7 @@ namespace MQTTnet.Formatter
public void Write(MqttBufferWriter propertyWriter) public void Write(MqttBufferWriter propertyWriter)
{ {
if (propertyWriter == null) ArgumentNullException.ThrowIfNull(propertyWriter);
{
throw new ArgumentNullException(nameof(propertyWriter));
}
WriteBinary(propertyWriter._buffer, 0, propertyWriter.Length); WriteBinary(propertyWriter._buffer, 0, propertyWriter.Length);
} }
@@ -140,10 +137,7 @@ namespace MQTTnet.Formatter
public void WriteBinary(byte[] buffer, int offset, int count) public void WriteBinary(byte[] buffer, int offset, int count)
{ {
if (buffer == null) ArgumentNullException.ThrowIfNull(buffer);
{
throw new ArgumentNullException(nameof(buffer));
}
if (count == 0) if (count == 0)
{ {
@@ -11,10 +11,7 @@ public static class MqttConnectPacketFactory
{ {
public static MqttConnectPacket Create(MqttClientOptions clientOptions) public static MqttConnectPacket Create(MqttClientOptions clientOptions)
{ {
if (clientOptions == null) ArgumentNullException.ThrowIfNull(clientOptions);
{
throw new ArgumentNullException(nameof(clientOptions));
}
var connectPacket = new MqttConnectPacket var connectPacket = new MqttConnectPacket
{ {
@@ -12,10 +12,7 @@ public static class MqttPubAckPacketFactory
{ {
public static MqttPubAckPacket Create(MqttApplicationMessageReceivedEventArgs applicationMessageReceivedEventArgs) public static MqttPubAckPacket Create(MqttApplicationMessageReceivedEventArgs applicationMessageReceivedEventArgs)
{ {
if (applicationMessageReceivedEventArgs == null) ArgumentNullException.ThrowIfNull(applicationMessageReceivedEventArgs);
{
throw new ArgumentNullException(nameof(applicationMessageReceivedEventArgs));
}
var pubAckPacket = new MqttPubAckPacket var pubAckPacket = new MqttPubAckPacket
{ {
@@ -12,10 +12,7 @@ public static class MqttPubCompPacketFactory
{ {
public static MqttPubCompPacket Create(MqttPubRelPacket pubRelPacket, MqttApplicationMessageReceivedReasonCode reasonCode) public static MqttPubCompPacket Create(MqttPubRelPacket pubRelPacket, MqttApplicationMessageReceivedReasonCode reasonCode)
{ {
if (pubRelPacket == null) ArgumentNullException.ThrowIfNull(pubRelPacket);
{
throw new ArgumentNullException(nameof(pubRelPacket));
}
return new MqttPubCompPacket return new MqttPubCompPacket
{ {
@@ -12,10 +12,7 @@ public static class MqttPubRecPacketFactory
{ {
public static MqttPubRecPacket Create(MqttApplicationMessageReceivedEventArgs applicationMessageReceivedEventArgs) public static MqttPubRecPacket Create(MqttApplicationMessageReceivedEventArgs applicationMessageReceivedEventArgs)
{ {
if (applicationMessageReceivedEventArgs == null) ArgumentNullException.ThrowIfNull(applicationMessageReceivedEventArgs);
{
throw new ArgumentNullException(nameof(applicationMessageReceivedEventArgs));
}
var pubRecPacket = Create(applicationMessageReceivedEventArgs.PublishPacket, applicationMessageReceivedEventArgs.ReasonCode); var pubRecPacket = Create(applicationMessageReceivedEventArgs.PublishPacket, applicationMessageReceivedEventArgs.ReasonCode);
pubRecPacket.UserProperties = applicationMessageReceivedEventArgs.ResponseUserProperties; pubRecPacket.UserProperties = applicationMessageReceivedEventArgs.ResponseUserProperties;
@@ -12,10 +12,7 @@ public static class MqttPubRelPacketFactory
{ {
public static MqttPubRelPacket Create(MqttPubRecPacket pubRecPacket, MqttApplicationMessageReceivedReasonCode reasonCode) public static MqttPubRelPacket Create(MqttPubRecPacket pubRecPacket, MqttApplicationMessageReceivedReasonCode reasonCode)
{ {
if (pubRecPacket == null) ArgumentNullException.ThrowIfNull(pubRecPacket);
{
throw new ArgumentNullException(nameof(pubRecPacket));
}
return new MqttPubRelPacket return new MqttPubRelPacket
{ {
@@ -12,10 +12,7 @@ namespace MQTTnet.Formatter
{ {
public static MqttPublishPacket Create(MqttApplicationMessage applicationMessage) public static MqttPublishPacket Create(MqttApplicationMessage applicationMessage)
{ {
if (applicationMessage == null) ArgumentNullException.ThrowIfNull(applicationMessage);
{
throw new ArgumentNullException(nameof(applicationMessage));
}
// Copy all values to their matching counterparts. // Copy all values to their matching counterparts.
// The not supported values in MQTT 3.1.1 are not serialized (excluded) later. // The not supported values in MQTT 3.1.1 are not serialized (excluded) later.
@@ -11,10 +11,7 @@ public static class MqttSubscribePacketFactory
{ {
public static MqttSubscribePacket Create(MqttClientSubscribeOptions clientSubscribeOptions) public static MqttSubscribePacket Create(MqttClientSubscribeOptions clientSubscribeOptions)
{ {
if (clientSubscribeOptions == null) ArgumentNullException.ThrowIfNull(clientSubscribeOptions);
{
throw new ArgumentNullException(nameof(clientSubscribeOptions));
}
var packet = new MqttSubscribePacket var packet = new MqttSubscribePacket
{ {
@@ -11,10 +11,7 @@ public static class MqttUnsubscribePacketFactory
{ {
public static MqttUnsubscribePacket Create(MqttClientUnsubscribeOptions clientUnsubscribeOptions) public static MqttUnsubscribePacket Create(MqttClientUnsubscribeOptions clientUnsubscribeOptions)
{ {
if (clientUnsubscribeOptions == null) ArgumentNullException.ThrowIfNull(clientUnsubscribeOptions);
{
throw new ArgumentNullException(nameof(clientUnsubscribeOptions));
}
var packet = new MqttUnsubscribePacket var packet = new MqttUnsubscribePacket
{ {
@@ -2,24 +2,23 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
namespace MQTTnet.Formatter namespace MQTTnet.Formatter;
public struct ReadFixedHeaderResult
{ {
public struct ReadFixedHeaderResult public static ReadFixedHeaderResult Canceled { get; } = new()
{ {
public static ReadFixedHeaderResult Canceled { get; } = new ReadFixedHeaderResult IsCanceled = true
{ };
IsCanceled = true
};
public static ReadFixedHeaderResult ConnectionClosed { get; } = new ReadFixedHeaderResult public static ReadFixedHeaderResult ConnectionClosed { get; } = new()
{ {
IsConnectionClosed = true IsConnectionClosed = true
}; };
public bool IsCanceled { get; set; } public bool IsCanceled { get; set; }
public bool IsConnectionClosed { get; set; } public bool IsConnectionClosed { get; init; }
public MqttFixedHeader FixedHeader { get; set; } public MqttFixedHeader FixedHeader { get; init; }
}
} }
@@ -18,9 +18,9 @@ namespace MQTTnet.Formatter.V3
{ {
const int FixedHeaderSize = 1; const int FixedHeaderSize = 1;
static readonly MqttDisconnectPacket DisconnectPacket = new MqttDisconnectPacket(); static readonly MqttDisconnectPacket DisconnectPacket = new();
readonly MqttBufferReader _bufferReader = new MqttBufferReader(); readonly MqttBufferReader _bufferReader = new();
readonly MqttBufferWriter _bufferWriter; readonly MqttBufferWriter _bufferWriter;
readonly MqttProtocolVersion _mqttProtocolVersion; readonly MqttProtocolVersion _mqttProtocolVersion;
@@ -68,10 +68,8 @@ namespace MQTTnet.Formatter.V3
{ {
return DecodeConnAckPacketV311(receivedMqttPacket.Body); return DecodeConnAckPacketV311(receivedMqttPacket.Body);
} }
else
{ return DecodeConnAckPacket(receivedMqttPacket.Body);
return DecodeConnAckPacket(receivedMqttPacket.Body);
}
case MqttControlPacketType.Disconnect: case MqttControlPacketType.Disconnect:
return DisconnectPacket; return DisconnectPacket;
@@ -91,10 +89,7 @@ namespace MQTTnet.Formatter.V3
public MqttPacketBuffer Encode(MqttPacket packet) public MqttPacketBuffer Encode(MqttPacket packet)
{ {
if (packet == null) ArgumentNullException.ThrowIfNull(packet);
{
throw new ArgumentNullException(nameof(packet));
}
// Leave enough head space for max header size (fixed + 4 variable remaining length = 5 bytes) // Leave enough head space for max header size (fixed + 4 variable remaining length = 5 bytes)
_bufferWriter.Reset(5); _bufferWriter.Reset(5);
@@ -109,10 +104,6 @@ namespace MQTTnet.Formatter.V3
payload = publishPacket.Payload; payload = publishPacket.Payload;
remainingLength += (uint)payload.Length; remainingLength += (uint)payload.Length;
} }
else
{
publishPacket = null;
}
var remainingLengthSize = MqttBufferWriter.GetVariableByteIntegerSize(remainingLength); var remainingLengthSize = MqttBufferWriter.GetVariableByteIntegerSize(remainingLength);
@@ -484,7 +475,16 @@ namespace MQTTnet.Formatter.V3
ValidateConnectPacket(packet); ValidateConnectPacket(packet);
bufferWriter.WriteString("MQTT"); bufferWriter.WriteString("MQTT");
bufferWriter.WriteByte(4); // 3.1.2.2 Protocol Level 4
// 3.1.2.2 Protocol Level 4
var protocolVersion = 4;
if (packet.TryPrivate)
{
protocolVersion |= 0x80;
}
bufferWriter.WriteByte((byte)protocolVersion);
byte connectFlags = 0x0; byte connectFlags = 0x0;
if (packet.CleanSession) if (packet.CleanSession)
@@ -555,19 +555,15 @@ namespace MQTTnet.Formatter.V3
{ {
return EncodeConnectPacketV311(connectPacket, bufferWriter); return EncodeConnectPacketV311(connectPacket, bufferWriter);
} }
else
{ return EncodeConnectPacket(connectPacket, bufferWriter);
return EncodeConnectPacket(connectPacket, bufferWriter);
}
case MqttConnAckPacket connAckPacket: case MqttConnAckPacket connAckPacket:
if (_mqttProtocolVersion == MqttProtocolVersion.V311) if (_mqttProtocolVersion == MqttProtocolVersion.V311)
{ {
return EncodeConnAckPacketV311(connAckPacket, bufferWriter); return EncodeConnAckPacketV311(connAckPacket, bufferWriter);
} }
else
{ return EncodeConnAckPacket(connAckPacket, bufferWriter);
return EncodeConnAckPacket(connAckPacket, bufferWriter);
}
case MqttDisconnectPacket _: case MqttDisconnectPacket _:
return EncodeEmptyPacket(MqttControlPacketType.Disconnect); return EncodeEmptyPacket(MqttControlPacketType.Disconnect);
case MqttPingReqPacket _: case MqttPingReqPacket _:
@@ -797,10 +793,7 @@ namespace MQTTnet.Formatter.V3
void ValidateConnectPacket(MqttConnectPacket packet) void ValidateConnectPacket(MqttConnectPacket packet)
{ {
if (packet == null) ArgumentNullException.ThrowIfNull(packet);
{
throw new ArgumentNullException(nameof(packet));
}
if (string.IsNullOrEmpty(packet.ClientId) && !packet.CleanSession) if (string.IsNullOrEmpty(packet.ClientId) && !packet.CleanSession)
{ {
@@ -13,7 +13,7 @@ namespace MQTTnet.Formatter.V5
{ {
public sealed class MqttV5PacketDecoder public sealed class MqttV5PacketDecoder
{ {
readonly MqttBufferReader _bufferReader = new MqttBufferReader(); readonly MqttBufferReader _bufferReader = new();
public MqttPacket Decode(ReceivedMqttPacket receivedMqttPacket) public MqttPacket Decode(ReceivedMqttPacket receivedMqttPacket)
{ {
@@ -25,10 +25,7 @@ public sealed class MqttV5PacketEncoder
public MqttPacketBuffer Encode(MqttPacket packet) public MqttPacketBuffer Encode(MqttPacket packet)
{ {
if (packet == null) ArgumentNullException.ThrowIfNull(packet);
{
throw new ArgumentNullException(nameof(packet));
}
// Leave enough head space for max header size (fixed + 4 variable remaining length = 5 bytes) // Leave enough head space for max header size (fixed + 4 variable remaining length = 5 bytes)
const int ReservedHeaderSize = 5; const int ReservedHeaderSize = 5;
@@ -230,10 +230,7 @@ namespace MQTTnet.Formatter.V5
public void WriteTo(MqttBufferWriter target) public void WriteTo(MqttBufferWriter target)
{ {
if (target == null) ArgumentNullException.ThrowIfNull(target);
{
throw new ArgumentNullException(nameof(target));
}
target.WriteVariableByteInteger((uint)_bufferWriter.Length); target.WriteVariableByteInteger((uint)_bufferWriter.Length);
target.Write(_bufferWriter); target.Write(_bufferWriter);
+1 -1
View File
@@ -29,7 +29,7 @@ public interface IMqttClient : IDisposable
Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken = default); Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken = default);
Task SendExtendedAuthenticationExchangeDataAsync(MqttExtendedAuthenticationExchangeData data, CancellationToken cancellationToken = default); Task SendEnhancedAuthenticationExchangeDataAsync(MqttEnhancedAuthenticationExchangeData data, CancellationToken cancellationToken = default);
Task<MqttClientSubscribeResult> SubscribeAsync(MqttClientSubscribeOptions options, CancellationToken cancellationToken = default); Task<MqttClientSubscribeResult> SubscribeAsync(MqttClientSubscribeOptions options, CancellationToken cancellationToken = default);
@@ -124,20 +124,14 @@ public sealed class CrossPlatformSocket : IDisposable
public void Bind(EndPoint localEndPoint) public void Bind(EndPoint localEndPoint)
{ {
if (localEndPoint is null) ArgumentNullException.ThrowIfNull(localEndPoint);
{
throw new ArgumentNullException(nameof(localEndPoint));
}
_socket.Bind(localEndPoint); _socket.Bind(localEndPoint);
} }
public async Task ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken) public async Task ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken)
{ {
if (endPoint is null) ArgumentNullException.ThrowIfNull(endPoint);
{
throw new ArgumentNullException(nameof(endPoint));
}
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
@@ -14,7 +14,7 @@ namespace MQTTnet.Implementations
{ {
public IMqttChannelAdapter CreateClientAdapter(MqttClientOptions options, MqttPacketInspector packetInspector, IMqttNetLogger logger) public IMqttChannelAdapter CreateClientAdapter(MqttClientOptions options, MqttPacketInspector packetInspector, IMqttNetLogger logger)
{ {
if (options == null) throw new ArgumentNullException(nameof(options)); ArgumentNullException.ThrowIfNull(options);
IMqttChannel channel; IMqttChannel channel;
switch (options.ChannelOptions) switch (options.ChannelOptions)

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