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
+183 -124
View File
@@ -8,26 +8,56 @@
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
{ {
const string mosquitto_org = @"
-----BEGIN CERTIFICATE-----
MIIEAzCCAuugAwIBAgIUBY1hlCGvdj4NhBXkZ/uLUZNILAwwDQYJKoZIhvcNAQEL
BQAwgZAxCzAJBgNVBAYTAkdCMRcwFQYDVQQIDA5Vbml0ZWQgS2luZ2RvbTEOMAwG
A1UEBwwFRGVyYnkxEjAQBgNVBAoMCU1vc3F1aXR0bzELMAkGA1UECwwCQ0ExFjAU
BgNVBAMMDW1vc3F1aXR0by5vcmcxHzAdBgkqhkiG9w0BCQEWEHJvZ2VyQGF0Y2hv
by5vcmcwHhcNMjAwNjA5MTEwNjM5WhcNMzAwNjA3MTEwNjM5WjCBkDELMAkGA1UE
BhMCR0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTES
MBAGA1UECgwJTW9zcXVpdHRvMQswCQYDVQQLDAJDQTEWMBQGA1UEAwwNbW9zcXVp
dHRvLm9yZzEfMB0GCSqGSIb3DQEJARYQcm9nZXJAYXRjaG9vLm9yZzCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAME0HKmIzfTOwkKLT3THHe+ObdizamPg
UZmD64Tf3zJdNeYGYn4CEXbyP6fy3tWc8S2boW6dzrH8SdFf9uo320GJA9B7U1FW
Te3xda/Lm3JFfaHjkWw7jBwcauQZjpGINHapHRlpiCZsquAthOgxW9SgDgYlGzEA
s06pkEFiMw+qDfLo/sxFKB6vQlFekMeCymjLCbNwPJyqyhFmPWwio/PDMruBTzPH
3cioBnrJWKXc3OjXdLGFJOfj7pP0j/dr2LH72eSvv3PQQFl90CZPFhrCUcRHSSxo
E6yjGOdnz7f6PveLIB574kQORwt8ePn0yidrTC1ictikED3nHYhMUOUCAwEAAaNT
MFEwHQYDVR0OBBYEFPVV6xBUFPiGKDyo5V3+Hbh4N9YSMB8GA1UdIwQYMBaAFPVV
6xBUFPiGKDyo5V3+Hbh4N9YSMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
BQADggEBAGa9kS21N70ThM6/Hj9D7mbVxKLBjVWe2TPsGfbl3rEDfZ+OKRZ2j6AC
6r7jb4TZO3dzF2p6dgbrlU71Y/4K0TdzIjRj3cQ3KSm41JvUQ0hZ/c04iGDg/xWf
+pp58nfPAYwuerruPNWmlStWAXf0UTqRtg4hQDWBuUFDJTuWuuBvEXudz74eh/wK
sMwfu1HFvjy5Z0iMDU8PUDepjVolOCue9ashlS4EB5IECdSR2TItnAIiIwimx839
LdUdRudafMu5T5Xma182OC0/u/xRlEm+tvKGGmfFcN0piqVl8OrSPBgIlb+1IKJE
m/XriWr/Cq4h/JfB7NTsezVslgkBaoU=
-----END CERTIFICATE-----
";
public static async Task Clean_Disconnect() public static async Task Clean_Disconnect()
{ {
/* /*
* This sample disconnects in a clean way. This will send a MQTT DISCONNECT packet * This sample disconnects in a clean way. This will send a MQTT DISCONNECT packet
* to the server and close the connection afterwards. * to the server and close the connection afterward.
* *
* See sample _Connect_Client_ for more details. * See sample _Connect_Client_ for more details.
*/ */
var mqttFactory = new MqttClientFactory(); var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build(); var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None); await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
@@ -35,9 +65,8 @@ public static class Client_Connection_Samples
// connection is closed in a "not clean" way. See MQTT specification for more details. // connection is closed in a "not clean" way. See MQTT specification for more details.
await mqttClient.DisconnectAsync(new MqttClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.NormalDisconnection).Build()); await mqttClient.DisconnectAsync(new MqttClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.NormalDisconnection).Build());
} }
}
public static async Task Connect_Client() public static async Task Connect()
{ {
/* /*
* This sample creates a simple MQTT client and connects to a public broker. * This sample creates a simple MQTT client and connects to a public broker.
@@ -48,8 +77,7 @@ public static class Client_Connection_Samples
var mqttFactory = new MqttClientFactory(); var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
// Use builder classes where possible in this project. // Use builder classes where possible in this project.
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build(); var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
@@ -68,37 +96,74 @@ public static class Client_Connection_Samples
await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None); await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
} }
}
public static async Task Connect_Client_Timeout() public static async Task Connect_Using_Enhanced_Authentication()
{ {
/* /*
* This sample creates a simple MQTT client and connects to an invalid broker using a timeout. * This sample uses enhanced authentication (Kerberos) when creating the 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()) /*
{ * Server part...
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").Build(); */
try var mqttServerFactory = new MqttServerFactory();
var serverOptions = mqttServerFactory.CreateServerOptionsBuilder().WithDefaultEndpoint().Build();
var server = mqttServerFactory.CreateMqttServer(serverOptions);
server.ValidatingConnectionAsync += async args =>
{ {
using (var timeoutToken = new CancellationTokenSource(TimeSpan.FromSeconds(1))) if (args.AuthenticationMethod == "GS2-KRB5")
{ {
await mqttClient.ConnectAsync(mqttClientOptions, timeoutToken.Token); 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
catch (OperationCanceledException)
{ {
Console.WriteLine("Timeout while connecting."); 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_Client_Using_MQTTv5() public static async Task Connect_Using_MQTTv5()
{ {
/* /*
* This sample creates a simple MQTT client and connects to a public broker using MQTTv5. * This sample creates a simple MQTT client and connects to a public broker using MQTTv5.
@@ -108,8 +173,7 @@ public static class Client_Connection_Samples
var mqttFactory = new MqttClientFactory(); var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").WithProtocolVersion(MqttProtocolVersion.V500).Build(); var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").WithProtocolVersion(MqttProtocolVersion.V500).Build();
// In MQTTv5 the response contains much more information. // In MQTTv5 the response contains much more information.
@@ -119,9 +183,8 @@ public static class Client_Connection_Samples
response.DumpToConsole(); response.DumpToConsole();
} }
}
public static async Task Connect_Client_Using_TLS_1_2() 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 sample creates a simple MQTT client and connects to a public broker using TLS 1.2 encryption.
@@ -131,8 +194,7 @@ public static class Client_Connection_Samples
var mqttFactory = new MqttClientFactory(); var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("mqtt.fluux.io") var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("mqtt.fluux.io")
.WithTlsOptions( .WithTlsOptions(
o => o =>
@@ -146,38 +208,13 @@ public static class Client_Connection_Samples
}) })
.Build(); .Build();
using (var timeout = new CancellationTokenSource(5000)) using var timeout = new CancellationTokenSource(5000);
{
await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token); await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
Console.WriteLine("The MQTT client is connected."); Console.WriteLine("The MQTT client is connected.");
} }
}
}
public static async Task Connect_Client_Using_WebSockets() public static async Task Connect_Using_TLS_Encryption()
{
/*
* 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 sample creates a simple MQTT client and connects to a public broker with enabled TLS encryption.
@@ -187,8 +224,7 @@ public static class Client_Connection_Samples
var mqttFactory = new MqttClientFactory(); var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883) var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883)
.WithTlsOptions( .WithTlsOptions(
o => o.WithCertificateValidationHandler( o => o.WithCertificateValidationHandler(
@@ -198,15 +234,48 @@ public static class Client_Connection_Samples
.Build(); .Build();
// In MQTTv5 the response contains much more information. // In MQTTv5 the response contains much more information.
using (var timeout = new CancellationTokenSource(5000)) using var timeout = new CancellationTokenSource(5000);
{
var response = await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token); var response = await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
Console.WriteLine("The MQTT client is connected."); Console.WriteLine("The MQTT client is connected.");
response.DumpToConsole(); 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() public static async Task Connect_With_Amazon_AWS()
@@ -219,8 +288,7 @@ public static class Client_Connection_Samples
var mqttFactory = new MqttClientFactory(); var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("amazon.web.services.broker") var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("amazon.web.services.broker")
// Disabling packet fragmentation is very important! // Disabling packet fragmentation is very important!
.WithoutPacketFragmentation() .WithoutPacketFragmentation()
@@ -232,7 +300,6 @@ public static class Client_Connection_Samples
await mqttClient.DisconnectAsync(); await mqttClient.DisconnectAsync();
} }
}
public static async Task Disconnect_Clean() public static async Task Disconnect_Clean()
{ {
@@ -244,8 +311,7 @@ public static class Client_Connection_Samples
var mqttFactory = new MqttClientFactory(); var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build(); var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None); await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
@@ -254,13 +320,12 @@ public static class Client_Connection_Samples
// Using a reason code requires MQTT version 5.0.0! // Using a reason code requires MQTT version 5.0.0!
await mqttClient.DisconnectAsync(MqttClientDisconnectOptionsReason.ImplementationSpecificError); await mqttClient.DisconnectAsync(MqttClientDisconnectOptionsReason.ImplementationSpecificError);
} }
}
public static async Task Disconnect_Non_Clean() public static async Task Disconnect_Non_Clean()
{ {
/* /*
* This sample disconnects from the server without sending a DISCONNECT packet. * This sample disconnects from the server without sending a DISCONNECT packet.
* This way of disconnecting is treated as a non clean disconnect which will * This way of disconnecting is treated as a non-clean disconnect which will
* trigger sending the last will etc. * trigger sending the last will etc.
*/ */
@@ -286,8 +351,7 @@ public static class Client_Connection_Samples
var mqttFactory = new MqttClientFactory(); var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("mqtt.fluux.io", 8883) var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("mqtt.fluux.io", 8883)
.WithTlsOptions( .WithTlsOptions(
o => o =>
@@ -306,12 +370,9 @@ public static class Client_Connection_Samples
.Build(); .Build();
// In MQTTv5 the response contains much more information. // In MQTTv5 the response contains much more information.
using (var timeout = new CancellationTokenSource(5000)) using var timeout = new CancellationTokenSource(5000);
{
await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token); await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
} }
}
}
public static async Task Ping_Server() public static async Task Ping_Server()
{ {
@@ -323,8 +384,7 @@ public static class Client_Connection_Samples
var mqttFactory = new MqttClientFactory(); var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build(); var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None); await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
@@ -334,20 +394,18 @@ public static class Client_Connection_Samples
Console.WriteLine("The MQTT server replied to the ping request."); Console.WriteLine("The MQTT server replied to the ping request.");
} }
}
public static async Task Reconnect_Using_Event() public static async Task Reconnect_Using_Event()
{ {
/* /*
* This sample shows how to reconnect when the connection was dropped. * This sample shows how to reconnect when the connection was dropped.
* This approach uses one of the events from the client. * 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). * This approach has a risk of deadlocks! Consider using the timer approach (see sample).
*/ */
var mqttFactory = new MqttClientFactory(); var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build(); var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
mqttClient.DisconnectedAsync += async e => mqttClient.DisconnectedAsync += async e =>
@@ -361,7 +419,6 @@ public static class Client_Connection_Samples
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None); await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
} }
}
public static void Reconnect_Using_Timer() public static void Reconnect_Using_Timer()
{ {
@@ -373,8 +430,7 @@ public static class Client_Connection_Samples
var mqttFactory = new MqttClientFactory(); var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient()) using var mqttClient = mqttFactory.CreateMqttClient();
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build(); var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
_ = Task.Run( _ = Task.Run(
@@ -409,55 +465,58 @@ public static class Client_Connection_Samples
Console.WriteLine("Press <Enter> to exit"); Console.WriteLine("Press <Enter> to exit");
Console.ReadLine(); Console.ReadLine();
} }
}
public static async Task ConnectTls_WithCaFile() 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(); var mqttFactory = new MqttClientFactory();
X509Certificate2Collection caChain = new X509Certificate2Collection(); using var mqttClient = mqttFactory.CreateMqttClient();
caChain.ImportFromPem(mosquitto_org); // from https://test.mosquitto.org/ssl/mosquitto.org.crt var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").Build();
using (var mqttClient = mqttFactory.CreateMqttClient()) try
{ {
var mqttClientOptions = new MqttClientOptionsBuilder() using var timeoutToken = new CancellationTokenSource(TimeSpan.FromSeconds(1));
.WithTcpServer("test.mosquitto.org", 8883) await mqttClient.ConnectAsync(mqttClientOptions, timeoutToken.Token);
.WithTlsOptions(new MqttClientTlsOptionsBuilder() }
.WithTrustChain(caChain) catch (OperationCanceledException)
.Build()) {
.Build(); Console.WriteLine("Timeout while connecting.");
}
var connAck = await mqttClient.ConnectAsync(mqttClientOptions);
Console.WriteLine("Connected to test.moquitto.org:8883 with CaFile mosquitto.org.crt: " + connAck.ResultCode);
} }
class SampleClientKerberosAuthenticationHandler : IMqttEnhancedAuthenticationHandler
{
public async Task HandleEnhancedAuthenticationAsync(MqttEnhancedAuthenticationEventArgs eventArgs)
{
if (eventArgs.AuthenticationMethod != "GS2-KRB5")
{
throw new InvalidOperationException("Wrong authentication method");
} }
const string mosquitto_org = @"
-----BEGIN CERTIFICATE-----
MIIEAzCCAuugAwIBAgIUBY1hlCGvdj4NhBXkZ/uLUZNILAwwDQYJKoZIhvcNAQEL
BQAwgZAxCzAJBgNVBAYTAkdCMRcwFQYDVQQIDA5Vbml0ZWQgS2luZ2RvbTEOMAwG
A1UEBwwFRGVyYnkxEjAQBgNVBAoMCU1vc3F1aXR0bzELMAkGA1UECwwCQ0ExFjAU
BgNVBAMMDW1vc3F1aXR0by5vcmcxHzAdBgkqhkiG9w0BCQEWEHJvZ2VyQGF0Y2hv
by5vcmcwHhcNMjAwNjA5MTEwNjM5WhcNMzAwNjA3MTEwNjM5WjCBkDELMAkGA1UE
BhMCR0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTES
MBAGA1UECgwJTW9zcXVpdHRvMQswCQYDVQQLDAJDQTEWMBQGA1UEAwwNbW9zcXVp
dHRvLm9yZzEfMB0GCSqGSIb3DQEJARYQcm9nZXJAYXRjaG9vLm9yZzCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAME0HKmIzfTOwkKLT3THHe+ObdizamPg
UZmD64Tf3zJdNeYGYn4CEXbyP6fy3tWc8S2boW6dzrH8SdFf9uo320GJA9B7U1FW
Te3xda/Lm3JFfaHjkWw7jBwcauQZjpGINHapHRlpiCZsquAthOgxW9SgDgYlGzEA
s06pkEFiMw+qDfLo/sxFKB6vQlFekMeCymjLCbNwPJyqyhFmPWwio/PDMruBTzPH
3cioBnrJWKXc3OjXdLGFJOfj7pP0j/dr2LH72eSvv3PQQFl90CZPFhrCUcRHSSxo
E6yjGOdnz7f6PveLIB574kQORwt8ePn0yidrTC1ictikED3nHYhMUOUCAwEAAaNT
MFEwHQYDVR0OBBYEFPVV6xBUFPiGKDyo5V3+Hbh4N9YSMB8GA1UdIwQYMBaAFPVV
6xBUFPiGKDyo5V3+Hbh4N9YSMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
BQADggEBAGa9kS21N70ThM6/Hj9D7mbVxKLBjVWe2TPsGfbl3rEDfZ+OKRZ2j6AC
6r7jb4TZO3dzF2p6dgbrlU71Y/4K0TdzIjRj3cQ3KSm41JvUQ0hZ/c04iGDg/xWf
+pp58nfPAYwuerruPNWmlStWAXf0UTqRtg4hQDWBuUFDJTuWuuBvEXudz74eh/wK
sMwfu1HFvjy5Z0iMDU8PUDepjVolOCue9ashlS4EB5IECdSR2TItnAIiIwimx839
LdUdRudafMu5T5Xma182OC0/u/xRlEm+tvKGGmfFcN0piqVl8OrSPBgIlb+1IKJE
m/XriWr/Cq4h/JfB7NTsezVslgkBaoU=
-----END CERTIFICATE-----
";
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,28 +2,30 @@
// 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)); _connectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket));
ChannelAdapter = clientAdapter ?? throw new ArgumentNullException(nameof(clientAdapter)); ChannelAdapter = clientAdapter ?? throw new ArgumentNullException(nameof(clientAdapter));
SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems)); SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems));
CancellationToken = cancellationToken;
} }
/// <summary> /// <summary>
@@ -44,6 +46,8 @@ namespace MQTTnet.Server
/// </summary> /// </summary>
public string AuthenticationMethod => _connectPacket.AuthenticationMethod; public string AuthenticationMethod => _connectPacket.AuthenticationMethod;
public CancellationToken CancellationToken { get; }
/// <summary> /// <summary>
/// Gets the channel adapter. This can be a _MqttConnectionContext_ (used in ASP.NET), a _MqttChannelAdapter_ (used for /// Gets the channel adapter. This can be a _MqttConnectionContext_ (used in ASP.NET), a _MqttChannelAdapter_ (used for
/// TCP or WebSockets) or a custom implementation. /// TCP or WebSockets) or a custom implementation.
@@ -70,7 +74,8 @@ namespace MQTTnet.Server
/// </summary> /// </summary>
public string ClientId => _connectPacket.ClientId; public string ClientId => _connectPacket.ClientId;
public string Endpoint => ChannelAdapter.Endpoint; [Obsolete("Use RemoteEndPoint instead.")]
public string Endpoint => RemoteEndPoint?.ToString();
public bool IsSecureConnection => ChannelAdapter.IsSecureConnection; public bool IsSecureConnection => ChannelAdapter.IsSecureConnection;
@@ -107,11 +112,13 @@ namespace MQTTnet.Server
/// <summary> /// <summary>
/// Gets or sets the receive maximum. /// Gets or sets the receive maximum.
/// This gives the maximum length of the receive messages. /// This gives the maximum length of the received messages.
/// A value of 0 indicates that the value is not used. /// A value of 0 indicates that the value is not used.
/// </summary> /// </summary>
public ushort ReceiveMaximum => _connectPacket.ReceiveMaximum; public ushort ReceiveMaximum => _connectPacket.ReceiveMaximum;
public EndPoint RemoteEndPoint => ChannelAdapter.RemoteEndPoint;
/// <summary> /// <summary>
/// Gets the request problem information. /// Gets the request problem information.
/// <remarks>MQTT 5.0.0+ feature.</remarks> /// <remarks>MQTT 5.0.0+ feature.</remarks>
@@ -181,10 +188,57 @@ namespace MQTTnet.Server
public List<MqttUserProperty> UserProperties => _connectPacket.UserProperties; public List<MqttUserProperty> UserProperties => _connectPacket.UserProperties;
/// <summary> /// <summary>
/// Gets or sets the will delay interval. /// 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. /// 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. /// A value of 0 indicates that the value is not used.
/// </summary> /// </summary>
public uint WillDelayInterval => _connectPacket.WillDelayInterval; public uint WillDelayInterval => _connectPacket.WillDelayInterval;
public async Task<ExchangeEnhancedAuthenticationResult> ExchangeEnhancedAuthenticationAsync(
ExchangeEnhancedAuthenticationOptions options,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(options);
var requestAuthPacket = new MqttAuthPacket
{
// From RFC: If the initial CONNECT packet included an Authentication Method property then all AUTH packets,
// 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].
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.");
}
if (responsePacket is MqttAuthPacket responseAuthPacket)
{
if (!string.Equals(AuthenticationMethod, responseAuthPacket.AuthenticationMethod, StringComparison.Ordinal))
{
throw new MqttProtocolViolationException("The authentication method cannot change while authenticating the client.");
}
return ExchangeEnhancedAuthenticationResultFactory.Create(responseAuthPacket);
}
if (responsePacket is MqttDisconnectPacket disconnectPacket)
{
return ExchangeEnhancedAuthenticationResultFactory.Create(disconnectPacket);
}
throw new MqttProtocolViolationException("Received other packet than AUTH while authenticating.");
} }
} }
@@ -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,11 +564,9 @@ 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)
{ {
_logger.Warning(exception, "Client '{0}': Error while sending DISCONNECT packet (ReasonCode = {1})", Id, options.ReasonCode); _logger.Warning(exception, "Client '{0}': Error while sending DISCONNECT packet (ReasonCode = {1})", Id, options.ReasonCode);
@@ -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;
} }
+18 -50
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 InjectApplicationMessage(InjectedMqttApplicationMessage injectedApplicationMessage, CancellationToken cancellationToken = default) public Task<MqttSessionStatus> GetSessionAsync(string id)
{ {
if (injectedApplicationMessage == null) ThrowIfNotStarted();
{
throw new ArgumentNullException(nameof(injectedApplicationMessage)); return _clientSessionsManager.GetSessionStatus(id);
} }
if (injectedApplicationMessage.ApplicationMessage == null) public Task InjectApplicationMessage(InjectedMqttApplicationMessage injectedApplicationMessage, CancellationToken cancellationToken = default)
{ {
throw new ArgumentNullException(nameof(injectedApplicationMessage.ApplicationMessage)); ArgumentNullException.ThrowIfNull(injectedApplicationMessage);
} ArgumentNullException.ThrowIfNull(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,25 +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.
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.
@@ -35,5 +33,4 @@ namespace MQTTnet.Server
/// 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,22 +24,18 @@ 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]
[ExpectedException(typeof(OperationCanceledException))] [ExpectedException(typeof(OperationCanceledException))]
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]
[ExpectedException(typeof(MqttCommunicationException))] [ExpectedException(typeof(MqttCommunicationException))]
@@ -53,12 +49,11 @@ 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; var disconnectHandlerCalled = false;
try try
{ {
client.DisconnectedAsync += args => client.DisconnectedAsync += _ =>
{ {
disconnectHandlerCalled = true; disconnectHandlerCalled = true;
return CompletedTask.Instance; return CompletedTask.Instance;
@@ -77,13 +72,11 @@ namespace MQTTnet.Tests.Clients.MqttClient
await LongTestDelay(); // disconnected handler is called async await LongTestDelay(); // disconnected handler is called async
Assert.IsTrue(disconnectHandlerCalled); 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(); var server = await testEnvironment.StartServer();
ClientDisconnectedEventArgs eventArgs = null; ClientDisconnectedEventArgs eventArgs = null;
@@ -105,13 +98,11 @@ namespace MQTTnet.Tests.Clients.MqttClient
Assert.IsNotNull(eventArgs); Assert.IsNotNull(eventArgs);
Assert.AreEqual(MqttClientDisconnectType.Clean, eventArgs.DisconnectType); 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(); var server = await testEnvironment.StartServer();
ClientDisconnectedEventArgs eventArgs = null; ClientDisconnectedEventArgs eventArgs = null;
@@ -133,13 +124,11 @@ namespace MQTTnet.Tests.Clients.MqttClient
Assert.IsNotNull(eventArgs); Assert.IsNotNull(eventArgs);
Assert.AreEqual(MqttDisconnectReasonCode.MessageRateTooHigh, eventArgs.ReasonCode); 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;
@@ -164,13 +153,78 @@ namespace MQTTnet.Tests.Clients.MqttClient
Assert.AreEqual("test_name", eventArgs.UserProperties[0].Name); Assert.AreEqual("test_name", eventArgs.UserProperties[0].Name);
Assert.AreEqual("test_value", eventArgs.UserProperties[0].Value); Assert.AreEqual("test_value", eventArgs.UserProperties[0].Value);
} }
class TestClientKerberosAuthenticationHandler : 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, eventArgs.CancellationToken);
var response = await eventArgs.ReceiveAsync(eventArgs.CancellationToken);
Assert.AreEqual(Encoding.UTF8.GetString(response.AuthenticationData), "reply context token");
// No further data is required, but we have to fulfil the exchange.
sendOptions = new SendMqttEnhancedAuthenticationDataOptions
{
Data = []
};
await eventArgs.SendAsync(sendOptions, eventArgs.CancellationToken);
}
}
[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; testEnvironment.IgnoreClientLogErrors = true;
var client = testEnvironment.CreateClient(); var client = testEnvironment.CreateClient();
@@ -178,11 +232,9 @@ namespace MQTTnet.Tests.Clients.MqttClient
try try
{ {
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(0.5))) using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(0.5));
{
await client.ConnectAsync(options, timeout.Token); await client.ConnectAsync(options, timeout.Token);
} }
}
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
} }
@@ -196,21 +248,16 @@ namespace MQTTnet.Tests.Clients.MqttClient
await LongTestDelay(); 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(); var server = await testEnvironment.StartServer();
server.ValidatingConnectionAsync += args => server.ValidatingConnectionAsync += args =>
{ {
args.ResponseUserProperties = new List<MqttUserProperty> args.ResponseUserProperties = [new("Property", "Value")];
{
new MqttUserProperty("Property", "Value")
};
args.ReasonCode = MqttConnectReasonCode.QuotaExceeded; args.ReasonCode = MqttConnectReasonCode.QuotaExceeded;
@@ -226,7 +273,6 @@ namespace MQTTnet.Tests.Clients.MqttClient
Assert.AreEqual(response.UserProperties[0].Name, "Property"); Assert.AreEqual(response.UserProperties[0].Name, "Property");
Assert.AreEqual(response.UserProperties[0].Value, "Value"); Assert.AreEqual(response.UserProperties[0].Value, "Value");
} }
}
[TestMethod] [TestMethod]
public async Task Throw_Proper_Exception_When_Not_Connected() public async Task Throw_Proper_Exception_When_Not_Connected()
@@ -234,11 +280,9 @@ 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)
{ {
if (exception.Message == "The MQTT client is not connected.") if (exception.Message == "The MQTT client is not connected.")
@@ -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