diff --git a/.editorconfig b/.editorconfig
index da3f0a4de..46144365e 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -21,14 +21,14 @@ dotnet_naming_rule.static_readonly_rule.symbols = static_readonly_symbols
dotnet_naming_style.lower_camel_case_style.capitalization = camel_case
dotnet_naming_style.lower_camel_case_style.required_prefix = _
dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case
-dotnet_naming_symbols.constants_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
+dotnet_naming_symbols.constants_symbols.applicable_accessibilities = public, internal, protected, protected_internal, private_protected
dotnet_naming_symbols.constants_symbols.applicable_kinds = field
dotnet_naming_symbols.constants_symbols.required_modifiers = const
-dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
+dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public, internal, protected, protected_internal, private_protected
dotnet_naming_symbols.public_fields_symbols.applicable_kinds = field
-dotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
+dotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities = public, internal, protected, protected_internal, private_protected
dotnet_naming_symbols.static_readonly_symbols.applicable_kinds = field
-dotnet_naming_symbols.static_readonly_symbols.required_modifiers = static,readonly
+dotnet_naming_symbols.static_readonly_symbols.required_modifiers = static, readonly
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
diff --git a/.github/workflows/ReleaseNotes.md b/.github/workflows/ReleaseNotes.md
deleted file mode 100644
index aab7f1e86..000000000
--- a/.github/workflows/ReleaseNotes.md
+++ /dev/null
@@ -1 +0,0 @@
-* [Client] Restored _Server_ and _Port_ behavior of client options (#2005).
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5813f5bd4..2416e31f6 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -3,62 +3,120 @@ name: CI
on: [push, pull_request]
env:
- VERSION: "4.3.6.${{github.run_number}}"
+ VERSION: "5.0.0.${{github.run_number}}"
+ PACKAGE_SUFFIX: "-beta"
jobs:
build:
- runs-on: windows-2022
+ runs-on: ubuntu-latest
steps:
- - name: Setup Windows SDK
- uses: GuillaumeFalourd/setup-windows10-sdk-action@v1
+ - name: Setup .NET SDK
+ uses: actions/setup-dotnet@v4
with:
- sdk-version: 18362
+ dotnet-version: |
+ 8.0.x
+
+ - name: Checkout code
+ uses: actions/checkout@v4
+ - name: Build package
+ run: dotnet build MQTTnet.sln --configuration Release /p:FileVersion=${{ env.VERSION }} /p:AssemblyVersion=${{ env.VERSION }} /p:PackageVersion=${{ env.VERSION }}${{ env.PACKAGE_SUFFIX }}
+
+ - name: Upload nuget packages
+ uses: actions/upload-artifact@v4
+ with:
+ name: nugets
+ path: |
+ ${{ github.workspace }}/Source/**/*.nupkg
+ ${{ github.workspace }}/Source/**/*.snupkg
+
+ test:
+ runs-on: ubuntu-latest
+
+ steps:
- name: Setup .NET SDK
- uses: actions/setup-dotnet@v1.9.0
+ uses: actions/setup-dotnet@v4
with:
dotnet-version: |
- 3.1.x
- 6.0.x
- 7.0.x
+ 8.0.x
- - name: Setup MSBuild
- uses: microsoft/setup-msbuild@v1.1
+ - name: Checkout code
+ uses: actions/checkout@v4
- - name: Checkout Code
- uses: actions/checkout@v2
+ - name: Execute tests
+ run: dotnet test --framework net8.0 Source/MQTTnet.Tests/MQTTnet.Tests.csproj
- - name: Setup Signing Certificate
- run: |
- $secret = '${{ secrets.SNC_BASE64 }}'
- $decoded = [System.Convert]::FromBase64CharArray($secret, 0, $secret.Length)
- Set-Content -Path ${{ github.workspace }}\certificate.snk -Value $decoded -AsByteStream
+ sign:
+ needs: build
+ runs-on: windows-latest # Code signing must run on a Windows agent for Authenticode signing (dll/exe)
+ if: github.repository == 'dotnet/MQTTnet'
+ steps:
+ - name: Setup .NET SDK
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: |
+ 6.0.x
- - name: Restore nuget packages
- run: msbuild MQTTnet.sln /t:Restore /p:Configuration="Release" /verbosity:m
+ - name: Download nuget packages
+ uses: actions/download-artifact@v4
+ with:
+ name: nugets
+ path: nugets
+
+ - name: Install sign CLI tool
+ run: dotnet tool install --tool-path . sign --version 0.9.0-beta.23127.3
+
+ - name: Sign nugets
+ shell: pwsh
+ run: >
+ ./sign code azure-key-vault `
+ "**/*.nupkg" `
+ --base-directory "${{ github.workspace }}\nugets" `
+ --publisher-name "MQTTnet" `
+ --description "MQTTnet" `
+ --description-url "https://github.com/dotnet/MQTTnet" `
+ --azure-key-vault-tenant-id "${{ secrets.AZURE_TENANT_ID }}" `
+ --azure-key-vault-client-id "${{ secrets.AZURE_CLIENT_ID }}" `
+ --azure-key-vault-client-secret "${{ secrets.AZURE_CLIENT_SECRET }}" `
+ --azure-key-vault-certificate "${{ secrets.KEY_VAULT_CERTIFICATE_ID }}" `
+ --azure-key-vault-url "${{ secrets.KEY_VAULT_URL }}"
+
+ - name: Upload signed nuget packages
+ uses: actions/upload-artifact@v4
+ with:
+ name: signed-nugets
+ path: |
+ ${{ github.workspace }}\nugets\**\*.nupkg
+ ${{ github.workspace }}\nugets\**\*.snupkg
- - name: Build solution
- run: msbuild MQTTnet.sln /t:Build /p:Configuration="Release" /verbosity:m /p:FileVersion=${{ env.VERSION }} /p:AssemblyVersion=${{ env.VERSION }} /p:PackageVersion=${{ env.VERSION }} /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=${{ github.workspace }}\certificate.snk
+ publish-myget:
+ if: ${{ github.event_name == 'push' }}
+ needs: sign
+ runs-on: ubuntu-latest
- - name: Collect nuget Packages
- uses: actions/upload-artifact@v2
+ steps:
+ - name: Download signed nuget packages
+ uses: actions/download-artifact@v4
with:
- name: nuget Packages
- path: |
- **\*.nupkg
- **\*.snupkg
+ name: signed-nugets
+ path: nugets
- - name: Setup VSTest
- uses: darenm/Setup-VSTest@v1
+ - name: Publish myget.com nugets
+ 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
- - name: Core Tests
- run: vstest.console.exe Source\MQTTnet.Tests\bin\Release\net7.0\MQTTnet.Tests.dll
+ publish-nuget:
+ if: ${{ github.event_name == 'release' }}
+ needs: sign
+ runs-on: ubuntu-latest
- - name: ASP.NET Tests
- run: vstest.console.exe Source\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\MQTTnet.AspNetCore.Tests.dll
+ steps:
+ - name: Download signed nuget packages
+ uses: actions/download-artifact@v4
+ with:
+ name: signed-nugets
+ path: nugets
- - name: Publish MyGet nugets
- if: ${{ github.event_name == 'push' }}
- run: dotnet nuget push **/*.nupkg -k ${{ secrets.MYGET_API_KEY }} -s https://www.myget.org/F/mqttnet/api/v3/index.json --skip-duplicate
\ No newline at end of file
+ - name: Publish nuget.org nugets
+ run: dotnet nuget push ${{ github.workspace }}/nugets/**/*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json --skip-duplicate
\ No newline at end of file
diff --git a/MQTTnet.sln b/MQTTnet.sln
index 4350c299c..5894cd1bc 100644
--- a/MQTTnet.sln
+++ b/MQTTnet.sln
@@ -10,22 +10,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
CODE-OF-CONDUCT.md = CODE-OF-CONDUCT.md
LICENSE = LICENSE
README.md = README.md
+ Source\ReleaseNotes.md = Source\ReleaseNotes.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore", "Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj", "{F10C4060-F7EE-4A83-919F-FF723E72F94A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.Rpc", "Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj", "{C444E9C8-95FA-430E-9126-274129DE16CD}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.ManagedClient", "Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj", "{C400533A-8EBA-4F0B-BF4D-295C3708604B}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.WebSocket4Net", "Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj", "{2BD01D53-4CA5-4142-BE8D-313876395E3E}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Samples", "Samples\MQTTnet.Samples.csproj", "{71CF35F5-3327-4A91-AAF4-5340F6701771}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Tests", "Source\MQTTnet.Tests\MQTTnet.Tests.csproj", "{B270F32A-9F3E-42EE-A989-813E35E29ADB}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore.Tests", "Source\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{A238BBBF-C75F-482D-9CC3-BB34ABA9B675}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Benchmarks", "Source\MQTTnet.Benchmarks\MQTTnet.Benchmarks.csproj", "{2F516E76-AAC4-4219-B7D1-34CDD3CFF381}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp", "Source\MQTTnet.TestApp\MQTTnet.TestApp.csproj", "{175D5340-CC5B-4542-939D-4E7D15A0BC8D}"
@@ -34,6 +29,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspTestApp", "Sourc
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.TopicTemplate", "Source\MQTTnet.Extensions.TopicTemplate\MQTTnet.Extensions.TopicTemplate.csproj", "{374D5861-F7A6-42A2-8CBE-040846000EF9}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Server", "Source\MQTTnet.Server\MQTTnet.Server.csproj", "{C876EFFD-C5AD-4E26-BD9F-4C48252C02BC}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -52,14 +49,6 @@ Global
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|Any CPU.Build.0 = Release|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|Any CPU.Build.0 = Release|Any CPU
- {2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|Any CPU.Build.0 = Release|Any CPU
{71CF35F5-3327-4A91-AAF4-5340F6701771}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{71CF35F5-3327-4A91-AAF4-5340F6701771}.Debug|Any CPU.Build.0 = Debug|Any CPU
{71CF35F5-3327-4A91-AAF4-5340F6701771}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -68,10 +57,6 @@ Global
{B270F32A-9F3E-42EE-A989-813E35E29ADB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B270F32A-9F3E-42EE-A989-813E35E29ADB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B270F32A-9F3E-42EE-A989-813E35E29ADB}.Release|Any CPU.Build.0 = Release|Any CPU
- {A238BBBF-C75F-482D-9CC3-BB34ABA9B675}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A238BBBF-C75F-482D-9CC3-BB34ABA9B675}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A238BBBF-C75F-482D-9CC3-BB34ABA9B675}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A238BBBF-C75F-482D-9CC3-BB34ABA9B675}.Release|Any CPU.Build.0 = Release|Any CPU
{2F516E76-AAC4-4219-B7D1-34CDD3CFF381}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2F516E76-AAC4-4219-B7D1-34CDD3CFF381}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2F516E76-AAC4-4219-B7D1-34CDD3CFF381}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -88,6 +73,10 @@ Global
{374D5861-F7A6-42A2-8CBE-040846000EF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{374D5861-F7A6-42A2-8CBE-040846000EF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{374D5861-F7A6-42A2-8CBE-040846000EF9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C876EFFD-C5AD-4E26-BD9F-4C48252C02BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C876EFFD-C5AD-4E26-BD9F-4C48252C02BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C876EFFD-C5AD-4E26-BD9F-4C48252C02BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C876EFFD-C5AD-4E26-BD9F-4C48252C02BC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -95,4 +84,6 @@ Global
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ EndGlobalSection
EndGlobal
diff --git a/README.md b/README.md
index 90c25b1ea..70bfe37a7 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,9 @@
# MQTTnet
-MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports the MQTT protocol up to version 5. It is compatible with mostly any supported .NET Framework version and CPU architecture.
+MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (
+broker) and supports the MQTT protocol up to version 5. It is compatible with mostly any supported .NET Framework
+version and CPU architecture.
## Features
@@ -28,13 +30,15 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
* Unit tested (~636 tests)
* No external dependencies
-\* Tested on local machine (Intel i7 8700K) with MQTTnet client and server running in the same process using the TCP channel. The app for verification is part of this repository and stored in _/Tests/MQTTnet.TestApp.NetCore_.
+\* Tested on local machine (Intel i7 8700K) with MQTTnet client and server running in the same process using the TCP
+channel. The app for verification is part of this repository and stored in _/Tests/MQTTnet.TestApp.NetCore_.
### Client
* Communication via TCP (+TLS) or WS (WebSocket) supported
* Included core _LowLevelMqttClient_ with low level functionality
-* Also included _ManagedMqttClient_ which maintains the connection and subscriptions automatically. Also application messages are queued and re-scheduled for higher QoS levels automatically.
+* Also included _ManagedMqttClient_ which maintains the connection and subscriptions automatically. Also application
+ messages are queued and re-scheduled for higher QoS levels automatically.
* Rx support (via another project)
* Compatible with Microsoft Azure IoT Hub
@@ -52,9 +56,11 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
## Getting Started
-MQTTnet is delivered via NuGet package manager. You can find the packages here: https://www.nuget.org/packages/MQTTnet/
+MQTTnet is delivered via NuGet package manager. You can find the packages
+here: https://www.nuget.org/packages/MQTTnet/
Use these command in the Package Manager console to install MQTTnet manually:
+
```
Install-Package MQTTnet
```
@@ -68,7 +74,8 @@ Samples for using MQTTnet are part of this repository. For starters these sample
## Code of Conduct
-This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community.
+This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our
+community.
For more information see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct).
## .NET Foundation
diff --git a/Samples/Client/Client_Connection_Samples.cs b/Samples/Client/Client_Connection_Samples.cs
index 65c3c652d..ee1ee20fb 100644
--- a/Samples/Client/Client_Connection_Samples.cs
+++ b/Samples/Client/Client_Connection_Samples.cs
@@ -8,8 +8,6 @@
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
-using MQTTnet.Client;
-using MQTTnet.Extensions.WebSocket4Net;
using MQTTnet.Formatter;
using MQTTnet.Samples.Helpers;
@@ -26,14 +24,14 @@ public static async Task Clean_Disconnect()
* See sample _Connect_Client_ for more details.
*/
- var mqttFactory = new MqttFactory();
+ var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
- // This will send the DISCONNECT packet. Calling _Dispose_ without DisconnectAsync the
+ // This will send the DISCONNECT packet. Calling _Dispose_ without DisconnectAsync the
// connection is closed in a "not clean" way. See MQTT specification for more details.
await mqttClient.DisconnectAsync(new MqttClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.NormalDisconnection).Build());
}
@@ -48,7 +46,7 @@ public static async Task Connect_Client()
* The default version of MQTT is 3.1.1.
*/
- var mqttFactory = new MqttFactory();
+ var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
@@ -56,7 +54,7 @@ public static async Task Connect_Client()
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
// This will throw an exception if the server is not available.
- // The result from this message returns additional data which was sent
+ // The result from this message returns additional data which was sent
// from the server. Please refer to the MQTT protocol specification for details.
var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
@@ -76,11 +74,11 @@ public static async Task Connect_Client_Timeout()
{
/*
* This sample creates a simple MQTT client and connects to an invalid broker using a timeout.
- *
+ *
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/
- var mqttFactory = new MqttFactory();
+ var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
@@ -104,11 +102,11 @@ public static async Task Connect_Client_Using_MQTTv5()
{
/*
* This sample creates a simple MQTT client and connects to a public broker using MQTTv5.
- *
+ *
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/
- var mqttFactory = new MqttFactory();
+ var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
@@ -127,11 +125,11 @@ public static async Task Connect_Client_Using_TLS_1_2()
{
/*
* This sample creates a simple MQTT client and connects to a public broker using TLS 1.2 encryption.
- *
+ *
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/
- var mqttFactory = new MqttFactory();
+ var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
@@ -157,39 +155,15 @@ public static async Task Connect_Client_Using_TLS_1_2()
}
}
- public static async Task Connect_Client_Using_WebSocket4Net()
- {
- /*
- * This sample creates a simple MQTT client and connects to a public broker using a WebSocket connection.
- * Instead of the .NET implementation of WebSockets the implementation from WebSocket4Net is used. It provides more
- * encryption algorithms and supports more platforms.
- *
- * This is a modified version of the sample _Connect_Client_! See other sample for more details.
- */
-
- var mqttFactory = new MqttFactory().UseWebSocket4Net();
-
- 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_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 MqttFactory();
+ var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
@@ -207,11 +181,11 @@ public static async Task Connect_Client_With_TLS_Encryption()
{
/*
* This sample creates a simple MQTT client and connects to a public broker with enabled TLS encryption.
- *
+ *
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/
- var mqttFactory = new MqttFactory();
+ var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
@@ -243,12 +217,12 @@ public static async Task Connect_With_Amazon_AWS()
* The broker requires special settings which are set here.
*/
- var mqttFactory = new MqttFactory();
+ var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("amazon.web.services.broker")
- // Disabling packet fragmentation is very important!
+ // Disabling packet fragmentation is very important!
.WithoutPacketFragmentation()
.Build();
@@ -268,7 +242,7 @@ public static async Task Disconnect_Clean()
* trigger sending the last will etc.
*/
- var mqttFactory = new MqttFactory();
+ var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
@@ -290,7 +264,7 @@ public static async Task Disconnect_Non_Clean()
* trigger sending the last will etc.
*/
- var mqttFactory = new MqttFactory();
+ var mqttFactory = new MqttClientFactory();
var mqttClient = mqttFactory.CreateMqttClient();
@@ -310,7 +284,7 @@ public static async Task Inspect_Certificate_Validation_Errors()
* including the reason for that status.
*/
- var mqttFactory = new MqttFactory();
+ var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
@@ -347,7 +321,7 @@ public static async Task Ping_Server()
* This is only supported in MQTTv5.0.0+.
*/
- var mqttFactory = new MqttFactory();
+ var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
@@ -370,7 +344,7 @@ public static async Task Reconnect_Using_Event()
* This approach has a risk of dead locks! Consider using the timer approach (see sample).
*/
- var mqttFactory = new MqttFactory();
+ var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
@@ -397,7 +371,7 @@ public static void Reconnect_Using_Timer()
* This is the recommended way but requires more custom code!
*/
- var mqttFactory = new MqttFactory();
+ var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
@@ -439,7 +413,7 @@ public static void Reconnect_Using_Timer()
public static async Task ConnectTls_WithCaFile()
{
- var mqttFactory = new MqttFactory();
+ var mqttFactory = new MqttClientFactory();
X509Certificate2Collection caChain = new X509Certificate2Collection();
caChain.ImportFromPem(mosquitto_org); // from https://test.mosquitto.org/ssl/mosquitto.org.crt
@@ -449,7 +423,7 @@ public static async Task ConnectTls_WithCaFile()
var mqttClientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("test.mosquitto.org", 8883)
.WithTlsOptions(new MqttClientTlsOptionsBuilder()
- .WithTrustChain(caChain)
+ .WithTrustChain(caChain)
.Build())
.Build();
diff --git a/Samples/Client/Client_Publish_Samples.cs b/Samples/Client/Client_Publish_Samples.cs
index 021c0613f..16cc67c78 100644
--- a/Samples/Client/Client_Publish_Samples.cs
+++ b/Samples/Client/Client_Publish_Samples.cs
@@ -6,8 +6,6 @@
// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming
-using MQTTnet.Client;
-
namespace MQTTnet.Samples.Client;
public static class Client_Publish_Samples
@@ -23,7 +21,7 @@ public static async Task Publish_Application_Message()
* or at least provides backward compatibility where possible.
*/
- var mqttFactory = new MqttFactory();
+ var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
@@ -41,11 +39,11 @@ public static async Task Publish_Application_Message()
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
await mqttClient.DisconnectAsync();
-
+
Console.WriteLine("MQTT application message is published.");
}
}
-
+
public static async Task Publish_Multiple_Application_Messages()
{
/*
@@ -54,7 +52,7 @@ public static async Task Publish_Multiple_Application_Messages()
* See sample _Publish_Application_Message_ for more details.
*/
- var mqttFactory = new MqttFactory();
+ var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
@@ -70,23 +68,23 @@ public static async Task Publish_Multiple_Application_Messages()
.Build();
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
-
+
applicationMessage = new MqttApplicationMessageBuilder()
.WithTopic("samples/temperature/living_room")
.WithPayload("20.0")
.Build();
-
+
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
applicationMessage = new MqttApplicationMessageBuilder()
.WithTopic("samples/temperature/living_room")
.WithPayload("21.0")
.Build();
-
+
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
await mqttClient.DisconnectAsync();
-
+
Console.WriteLine("MQTT application message is published.");
}
}
diff --git a/Samples/Client/Client_Subscribe_Samples.cs b/Samples/Client/Client_Subscribe_Samples.cs
index 60b4ac2ad..96a2a6f26 100644
--- a/Samples/Client/Client_Subscribe_Samples.cs
+++ b/Samples/Client/Client_Subscribe_Samples.cs
@@ -7,7 +7,6 @@
// ReSharper disable InconsistentNaming
// ReSharper disable UnusedMember.Local
-using MQTTnet.Client;
using MQTTnet.Extensions.TopicTemplate;
using MQTTnet.Packets;
using MQTTnet.Protocol;
@@ -17,15 +16,15 @@ namespace MQTTnet.Samples.Client;
public static class Client_Subscribe_Samples
{
- static MqttTopicTemplate sampleTemplate = new MqttTopicTemplate("mqttnet/samples/topic/{id}");
-
+ static readonly MqttTopicTemplate sampleTemplate = new("mqttnet/samples/topic/{id}");
+
public static async Task Handle_Received_Application_Message()
{
/*
* This sample subscribes to a topic and processes the received message.
*/
- var mqttFactory = new MqttFactory();
+ var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
@@ -44,9 +43,7 @@ public static async Task Handle_Received_Application_Message()
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
- var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
- .WithTopicTemplate(sampleTemplate.WithParameter("id", "2"))
- .Build();
+ var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder().WithTopicTemplate(sampleTemplate.WithParameter("id", "2")).Build();
await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
@@ -63,7 +60,7 @@ public static async Task Send_Responses()
* This sample subscribes to a topic and sends a response to the broker. This requires at least QoS level 1 to work!
*/
- var mqttFactory = new MqttFactory();
+ var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
@@ -86,10 +83,7 @@ public static async Task Send_Responses()
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
- var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
- .WithTopicTemplate(
- sampleTemplate.WithParameter("id", "1"))
- .Build();
+ var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder().WithTopicTemplate(sampleTemplate.WithParameter("id", "1")).Build();
var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
@@ -106,7 +100,7 @@ public static async Task Subscribe_Multiple_Topics()
* This sample subscribes to several topics in a single request.
*/
- var mqttFactory = new MqttFactory();
+ var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
@@ -117,12 +111,9 @@ public static async Task Subscribe_Multiple_Topics()
// Create the subscribe options including several topics with different options.
// It is also possible to all of these topics using a dedicated call of _SubscribeAsync_ per topic.
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
- .WithTopicTemplate(
- sampleTemplate.WithParameter("id", "1"))
- .WithTopicTemplate(
- sampleTemplate.WithParameter("id", "2"), noLocal: true)
- .WithTopicTemplate(
- sampleTemplate.WithParameter("id", "3"), retainHandling: MqttRetainHandling.SendAtSubscribe)
+ .WithTopicTemplate(sampleTemplate.WithParameter("id", "1"))
+ .WithTopicTemplate(sampleTemplate.WithParameter("id", "2"), noLocal: true)
+ .WithTopicTemplate(sampleTemplate.WithParameter("id", "3"), retainHandling: MqttRetainHandling.SendAtSubscribe)
.Build();
var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
@@ -140,7 +131,7 @@ public static async Task Subscribe_Topic()
* This sample subscribes to a topic.
*/
- var mqttFactory = new MqttFactory();
+ var mqttFactory = new MqttClientFactory();
using (var mqttClient = mqttFactory.CreateMqttClient())
{
@@ -148,9 +139,7 @@ public static async Task Subscribe_Topic()
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
- var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
- .WithTopicTemplate(sampleTemplate.WithParameter("id", "1"))
- .Build();
+ var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder().WithTopicTemplate(sampleTemplate.WithParameter("id", "1")).Build();
var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
diff --git a/Samples/Diagnostics/Logger_Samples.cs b/Samples/Diagnostics/Logger_Samples.cs
index 6cd0aa3e4..505ad7489 100644
--- a/Samples/Diagnostics/Logger_Samples.cs
+++ b/Samples/Diagnostics/Logger_Samples.cs
@@ -7,7 +7,7 @@
// ReSharper disable InconsistentNaming
using System.Text;
-using MQTTnet.Diagnostics;
+using MQTTnet.Diagnostics.Logger;
namespace MQTTnet.Samples.Diagnostics;
@@ -20,7 +20,7 @@ public static async Task Create_Custom_Logger()
* to other loggers like Microsoft logger or Serilog or log4net etc.
*/
- var mqttFactory = new MqttFactory(new MyLogger());
+ var mqttFactory = new MqttClientFactory(new MyLogger());
var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder()
.WithTcpServer("broker.hivemq.com")
@@ -62,7 +62,7 @@ public static async Task Use_Event_Logger()
Console.Write(output);
};
- var mqttFactory = new MqttFactory(mqttEventLogger);
+ var mqttFactory = new MqttClientFactory(mqttEventLogger);
var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder()
.WithTcpServer("broker.hivemq.com")
diff --git a/Samples/Diagnostics/PackageInspection_Samples.cs b/Samples/Diagnostics/PackageInspection_Samples.cs
index 1480246e3..1ab996a28 100644
--- a/Samples/Diagnostics/PackageInspection_Samples.cs
+++ b/Samples/Diagnostics/PackageInspection_Samples.cs
@@ -6,7 +6,7 @@
// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming
-using MQTTnet.Diagnostics;
+using MQTTnet.Diagnostics.PacketInspection;
namespace MQTTnet.Samples.Diagnostics;
@@ -17,19 +17,19 @@ public static async Task Inspect_Outgoing_Package()
/*
* This sample covers the inspection of outgoing packages from the client.
*/
-
- var mqttFactory = new MqttFactory();
-
+
+ var mqttFactory = new MqttClientFactory();
+
using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder()
.WithTcpServer("broker.hivemq.com")
.Build();
-
+
mqttClient.InspectPacketAsync += OnInspectPacket;
-
+
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
-
+
Console.WriteLine("MQTT client is connected.");
var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder()
diff --git a/Samples/MQTTnet.Samples.csproj b/Samples/MQTTnet.Samples.csproj
index 158023262..6d3920a22 100644
--- a/Samples/MQTTnet.Samples.csproj
+++ b/Samples/MQTTnet.Samples.csproj
@@ -2,24 +2,27 @@
Exe
- net7.0
+ net8.0
enable
enable
false
true
false
false
- 1591;NETSDK1138
+ 1591;NETSDK1138;NU1803;NU1901;NU1902
+ true
+ all
+ true
+ latest-Recommended
-
-
-
+
+
-
-
+
+
diff --git a/Samples/ManagedClient/Managed_Client_Simple_Samples.cs b/Samples/ManagedClient/Managed_Client_Simple_Samples.cs
deleted file mode 100644
index 723a33a63..000000000
--- a/Samples/ManagedClient/Managed_Client_Simple_Samples.cs
+++ /dev/null
@@ -1,52 +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.
-
-// ReSharper disable UnusedType.Global
-// ReSharper disable UnusedMember.Global
-// ReSharper disable InconsistentNaming
-
-using MQTTnet.Client;
-using MQTTnet.Extensions.ManagedClient;
-
-namespace MQTTnet.Samples.ManagedClient;
-
-public sealed class Managed_Client_Simple_Samples
-{
- public static async Task Connect_Client()
- {
- /*
- * This sample creates a simple managed MQTT client and connects to a public broker.
- *
- * The managed client extends the existing _MqttClient_. It adds the following features.
- * - Reconnecting when connection is lost.
- * - Storing pending messages in an internal queue so that an enqueue is possible while the client remains not connected.
- */
-
- var mqttFactory = new MqttFactory();
-
- using (var managedMqttClient = mqttFactory.CreateManagedMqttClient())
- {
- var mqttClientOptions = new MqttClientOptionsBuilder()
- .WithTcpServer("broker.hivemq.com")
- .Build();
-
- var managedMqttClientOptions = new ManagedMqttClientOptionsBuilder()
- .WithClientOptions(mqttClientOptions)
- .Build();
-
- await managedMqttClient.StartAsync(managedMqttClientOptions);
-
- // The application message is not sent. It is stored in an internal queue and
- // will be sent when the client is connected.
- await managedMqttClient.EnqueueAsync("Topic", "Payload");
-
- Console.WriteLine("The managed MQTT client is connected.");
-
- // Wait until the queue is fully processed.
- SpinWait.SpinUntil(() => managedMqttClient.PendingApplicationMessagesCount == 0, 10000);
-
- Console.WriteLine($"Pending messages = {managedMqttClient.PendingApplicationMessagesCount}");
- }
- }
-}
\ No newline at end of file
diff --git a/Samples/ManagedClient/Managed_Client_Subscribe_Samples.cs b/Samples/ManagedClient/Managed_Client_Subscribe_Samples.cs
deleted file mode 100644
index ff2102e01..000000000
--- a/Samples/ManagedClient/Managed_Client_Subscribe_Samples.cs
+++ /dev/null
@@ -1,87 +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.
-
-// ReSharper disable UnusedType.Global
-// ReSharper disable UnusedMember.Global
-// ReSharper disable InconsistentNaming
-
-using MQTTnet.Client;
-using MQTTnet.Extensions.ManagedClient;
-
-namespace MQTTnet.Samples.ManagedClient;
-
-public sealed class Managed_Client_Subscribe_Samples
-{
- public static async Task Connect_Client()
- {
- /*
- * This sample creates a simple managed MQTT client and connects to a public broker, subscribe to a topic and verifies subscription result.
- *
- * The managed client extends the existing _MqttClient_. It adds the following features.
- * - Reconnecting when connection is lost.
- * - Storing pending messages in an internal queue so that an enqueue is possible while the client remains not connected.
- */
-
- var mqttFactory = new MqttFactory();
- var subscribed = false;
-
- using (var managedMqttClient = mqttFactory.CreateManagedMqttClient())
- {
- var mqttClientOptions = new MqttClientOptionsBuilder()
- .WithTcpServer("broker.hivemq.com")
- .Build();
-
- var managedMqttClientOptions = new ManagedMqttClientOptionsBuilder()
- .WithClientOptions(mqttClientOptions)
- .Build();
-
- await managedMqttClient.StartAsync(managedMqttClientOptions);
-
- // The application message is not sent. It is stored in an internal queue and
- // will be sent when the client is connected.
- await managedMqttClient.EnqueueAsync("Topic", "Payload");
-
- Console.WriteLine("The managed MQTT client is connected.");
-
- // Wait until the queue is fully processed.
- SpinWait.SpinUntil(() => managedMqttClient.PendingApplicationMessagesCount == 0, 10000);
-
- Console.WriteLine($"Pending messages = {managedMqttClient.PendingApplicationMessagesCount}");
-
- managedMqttClient.SubscriptionsChangedAsync += args => SubscriptionsResultAsync(args, ref subscribed);
- await managedMqttClient.SubscribeAsync("Topic").ConfigureAwait(false);
-
- SpinWait.SpinUntil(() => subscribed, 1000);
- Console.WriteLine($"Subscription properly done {subscribed}");
- }
- }
-
- private static Task SubscriptionsResultAsync(SubscriptionsChangedEventArgs arg, ref bool subscribed)
- {
- foreach (var mqttClientSubscribeResult in arg.SubscribeResult)
- {
- Console.WriteLine($"Subscription reason {mqttClientSubscribeResult.ReasonString}");
- foreach (var item in mqttClientSubscribeResult.Items)
- {
- Console.WriteLine($"For topic filter {item.TopicFilter}, result code: {item.ResultCode}");
-
- if (item.TopicFilter.Topic == "Topic" && item.ResultCode == MqttClientSubscribeResultCode.GrantedQoS0 && !subscribed)
- {
- subscribed = true;
- }
- }
- }
-
- foreach (var mqttClientUnsubscribeResult in arg.UnsubscribeResult)
- {
- Console.WriteLine($"Unsubscription reason {mqttClientUnsubscribeResult.ReasonString}");
- foreach (var item in mqttClientUnsubscribeResult.Items)
- {
- Console.WriteLine($"For topic filter {item.TopicFilter}, result code: {item.ResultCode}");
- }
- }
-
- return Task.CompletedTask;
- }
-}
\ No newline at end of file
diff --git a/Samples/RpcClient/RpcClient_Samples.cs b/Samples/RpcClient/RpcClient_Samples.cs
index 8a182fc7f..b50c91218 100644
--- a/Samples/RpcClient/RpcClient_Samples.cs
+++ b/Samples/RpcClient/RpcClient_Samples.cs
@@ -6,7 +6,6 @@
// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming
-using MQTTnet.Client;
using MQTTnet.Extensions.Rpc;
using MQTTnet.Protocol;
@@ -19,14 +18,14 @@ public static class RpcClient_Samples
* This is done via defining a pattern which uses the topic to correlate the request and the response.
* From client usage it is possible to define a timeout.
*/
-
+
public static async Task Send_Request()
{
- var mqttFactory = new MqttFactory();
-
+ var mqttFactory = new MqttClientFactory();
+
// The RPC client is an addon for the existing client. So we need a regular client
// which is wrapped later.
-
+
using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder()
@@ -34,23 +33,23 @@ public static async Task Send_Request()
.Build();
await mqttClient.ConnectAsync(mqttClientOptions);
-
+
using (var mqttRpcClient = mqttFactory.CreateMqttRpcClient(mqttClient))
{
// Access to a fully featured application message is not supported for RPC calls!
// The method will throw an exception when the response was not received in time.
await mqttRpcClient.ExecuteAsync(TimeSpan.FromSeconds(2), "ping", "", MqttQualityOfServiceLevel.AtMostOnce);
}
-
+
Console.WriteLine("The RPC call was successful.");
}
}
-
+
/*
* The device must respond to the request using the correct topic. The following C code shows how a
* smart device like an ESP8266 must respond to the above sample.
*
- // If using the MQTT client PubSubClient it must be ensured
+ // If using the MQTT client PubSubClient it must be ensured
// that the request topic for each method is subscribed like the following.
mqttClient.subscribe("MQTTnet.RPC/+/ping");
mqttClient.subscribe("MQTTnet.RPC/+/do_something");
diff --git a/Samples/Server/Server_ASP_NET_Samples.cs b/Samples/Server/Server_ASP_NET_Samples.cs
index 147caf5ab..9247093e2 100644
--- a/Samples/Server/Server_ASP_NET_Samples.cs
+++ b/Samples/Server/Server_ASP_NET_Samples.cs
@@ -51,7 +51,7 @@ public MqttController()
{
// Inject other services via constructor.
}
-
+
public Task OnClientConnected(ClientConnectedEventArgs eventArgs)
{
Console.WriteLine($"Client '{eventArgs.ClientId}' connected.");
diff --git a/Samples/Server/Server_Diagnostics_Samples.cs b/Samples/Server/Server_Diagnostics_Samples.cs
index 0f31e9bec..07bc8e0b6 100644
--- a/Samples/Server/Server_Diagnostics_Samples.cs
+++ b/Samples/Server/Server_Diagnostics_Samples.cs
@@ -13,7 +13,6 @@ namespace MQTTnet.Samples.Server;
// ReSharper disable InconsistentNaming
// ReSharper disable EmptyConstructor
// ReSharper disable MemberCanBeMadeStatic.Local
-
public static class Server_Diagnostics_Samples
{
public static async Task Get_Notified_When_Client_Received_Message()
@@ -22,26 +21,26 @@ public static async Task Get_Notified_When_Client_Received_Message()
* This sample starts a MQTT server and attaches an event which gets fired whenever
* a client received and acknowledged a PUBLISH packet.
*/
-
- var mqttFactory = new MqttFactory();
+
+ var mqttServerFactory = new MqttServerFactory();
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
-
- using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
+
+ using (var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions))
{
// Attach the event handler.
mqttServer.ClientAcknowledgedPublishPacketAsync += e =>
{
Console.WriteLine($"Client '{e.ClientId}' acknowledged packet {e.PublishPacket.PacketIdentifier} with topic '{e.PublishPacket.Topic}'");
-
+
// It is also possible to read additional data from the client response. This requires casting the response packet.
var qos1AcknowledgePacket = e.AcknowledgePacket as MqttPubAckPacket;
Console.WriteLine($"QoS 1 reason code: {qos1AcknowledgePacket?.ReasonCode}");
-
+
var qos2AcknowledgePacket = e.AcknowledgePacket as MqttPubCompPacket;
Console.WriteLine($"QoS 2 reason code: {qos1AcknowledgePacket?.ReasonCode}");
return CompletedTask.Instance;
};
-
+
await mqttServer.StartAsync();
Console.WriteLine("Press Enter to exit.");
diff --git a/Samples/Server/Server_Intercepting_Samples.cs b/Samples/Server/Server_Intercepting_Samples.cs
index 62129c6d8..31bfa39bd 100644
--- a/Samples/Server/Server_Intercepting_Samples.cs
+++ b/Samples/Server/Server_Intercepting_Samples.cs
@@ -22,10 +22,10 @@ public static async Task Intercept_Application_Messages()
* Please see _Server_Simple_Samples_ for more details on how to start a server.
*/
- var mqttFactory = new MqttFactory();
+ var mqttServerFactory = new MqttServerFactory();
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
- using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
+ using (var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions))
{
mqttServer.InterceptingPublishAsync += args =>
{
diff --git a/Samples/Server/Server_Retained_Messages_Samples.cs b/Samples/Server/Server_Retained_Messages_Samples.cs
index 631dd9963..1c7341b72 100644
--- a/Samples/Server/Server_Retained_Messages_Samples.cs
+++ b/Samples/Server/Server_Retained_Messages_Samples.cs
@@ -6,9 +6,11 @@
// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming
+using System.Buffers;
using System.Text.Json;
using MQTTnet.Packets;
using MQTTnet.Protocol;
+using MQTTnet.Server;
namespace MQTTnet.Samples.Server;
@@ -22,12 +24,12 @@ public static async Task Persist_Retained_Messages()
var storePath = Path.Combine(Path.GetTempPath(), "RetainedMessages.json");
- var mqttFactory = new MqttFactory();
+ var mqttServerFactory = new MqttServerFactory();
// Due to security reasons the "default" endpoint (which is unencrypted) is not enabled by default!
- var mqttServerOptions = mqttFactory.CreateServerOptionsBuilder().WithDefaultEndpoint().Build();
+ var mqttServerOptions = mqttServerFactory.CreateServerOptionsBuilder().WithDefaultEndpoint().Build();
- using (var server = mqttFactory.CreateMqttServer(mqttServerOptions))
+ using (var server = mqttServerFactory.CreateMqttServer(mqttServerOptions))
{
// Make sure that the server will load the retained messages.
server.LoadingRetainedMessageAsync += async eventArgs =>
@@ -109,9 +111,9 @@ public static MqttRetainedMessageModel Create(MqttApplicationMessage message)
{
Topic = message.Topic,
- // Create a copy of the buffer from the payload segment because
+ // Create a copy of the buffer from the payload segment because
// it cannot be serialized and deserialized with the JSON serializer.
- Payload = message.PayloadSegment.ToArray(),
+ Payload = message.Payload.ToArray(),
UserProperties = message.UserProperties,
ResponseTopic = message.ResponseTopic,
CorrelationData = message.CorrelationData,
diff --git a/Samples/Server/Server_Simple_Samples.cs b/Samples/Server/Server_Simple_Samples.cs
index b2d6787cd..e7ed4bfec 100644
--- a/Samples/Server/Server_Simple_Samples.cs
+++ b/Samples/Server/Server_Simple_Samples.cs
@@ -6,7 +6,7 @@
// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming
-using MQTTnet.Diagnostics;
+using MQTTnet.Diagnostics.Logger;
using MQTTnet.Protocol;
using MQTTnet.Server;
@@ -64,7 +64,7 @@ public static async Task Run_Minimal_Server()
* This sample starts a simple MQTT server which will accept any TCP connection.
*/
- var mqttFactory = new MqttFactory();
+ var mqttServerFactory = new MqttServerFactory();
// The port for the default endpoint is 1883.
// The default endpoint is NOT encrypted!
@@ -77,7 +77,7 @@ public static async Task Run_Minimal_Server()
// .WithDefaultEndpointPort(1234)
// .Build();
- using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
+ using (var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions))
{
await mqttServer.StartAsync();
@@ -99,11 +99,11 @@ public static async Task Run_Server_With_Logging()
* See sample "Run_Minimal_Server" for more details.
*/
- var mqttFactory = new MqttFactory(new ConsoleLogger());
+ var mqttServerFactory = new MqttServerFactory(new ConsoleLogger());
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
- using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
+ using (var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions))
{
await mqttServer.StartAsync();
@@ -123,13 +123,13 @@ public static async Task Validating_Connections()
* See _Run_Minimal_Server_ for more information.
*/
- var mqttFactory = new MqttFactory();
+ var mqttServerFactory = new MqttServerFactory();
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
- using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
+ using (var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions))
{
- // Setup connection validation before starting the server so that there is
+ // Setup connection validation before starting the server so that there is
// no change to connect without valid credentials.
mqttServer.ValidatingConnectionAsync += e =>
{
@@ -162,11 +162,11 @@ public static async Task Validating_Connections()
static async Task StartMqttServer()
{
- var mqttFactory = new MqttFactory();
+ var mqttServerFactory = new MqttServerFactory();
// Due to security reasons the "default" endpoint (which is unencrypted) is not enabled by default!
- var mqttServerOptions = mqttFactory.CreateServerOptionsBuilder().WithDefaultEndpoint().Build();
- var server = mqttFactory.CreateMqttServer(mqttServerOptions);
+ var mqttServerOptions = mqttServerFactory.CreateServerOptionsBuilder().WithDefaultEndpoint().Build();
+ var server = mqttServerFactory.CreateMqttServer(mqttServerOptions);
await server.StartAsync();
return server;
}
@@ -216,4 +216,4 @@ public void Publish(MqttNetLogLevel logLevel, string source, string message, obj
}
}
}
-}
+}
\ No newline at end of file
diff --git a/Samples/Server/Server_TLS_Samples.cs b/Samples/Server/Server_TLS_Samples.cs
index 177f8f0b6..83d0d6010 100644
--- a/Samples/Server/Server_TLS_Samples.cs
+++ b/Samples/Server/Server_TLS_Samples.cs
@@ -24,14 +24,14 @@ public static async Task Run_Server_With_Self_Signed_Certificate()
* See sample "Run_Minimal_Server" for more details.
*/
- var mqttFactory = new MqttFactory();
+ var mqttServerFactory = new MqttServerFactory();
// This certificate is self signed so that
var certificate = CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.1");
-
+
var mqttServerOptions = new MqttServerOptionsBuilder().WithEncryptionCertificate(certificate).WithEncryptedEndpoint().Build();
- using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
+ using (var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions))
{
await mqttServer.StartAsync();
diff --git a/Source/MQTTnet.AspNetCore.Tests/MQTTnet.AspNetCore.Tests.csproj b/Source/MQTTnet.AspNetCore.Tests/MQTTnet.AspNetCore.Tests.csproj
deleted file mode 100644
index 63f972c25..000000000
--- a/Source/MQTTnet.AspNetCore.Tests/MQTTnet.AspNetCore.Tests.csproj
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
- netcoreapp3.1
- false
- true
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Source/MQTTnet.AspNetCore.Tests/Mockups/ConnectionHandlerMockup.cs b/Source/MQTTnet.AspNetCore.Tests/Mockups/ConnectionHandlerMockup.cs
deleted file mode 100644
index 387e0bfb7..000000000
--- a/Source/MQTTnet.AspNetCore.Tests/Mockups/ConnectionHandlerMockup.cs
+++ /dev/null
@@ -1,50 +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.Threading.Tasks;
-using Microsoft.AspNetCore.Connections;
-using MQTTnet.Adapter;
-using MQTTnet.Diagnostics;
-using MQTTnet.Formatter;
-using MQTTnet.Server;
-
-namespace MQTTnet.AspNetCore.Tests.Mockups
-{
- public sealed class ConnectionHandlerMockup : IMqttServerAdapter
- {
- public Func ClientHandler { get; set; }
- public TaskCompletionSource Context { get; } = new TaskCompletionSource();
-
- public void Dispose()
- {
- }
-
- public async Task OnConnectedAsync(ConnectionContext connection)
- {
- try
- {
- var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535));
- var context = new MqttConnectionContext(formatter, connection);
- Context.TrySetResult(context);
-
- await ClientHandler(context);
- }
- catch (Exception ex)
- {
- Context.TrySetException(ex);
- }
- }
-
- public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
- {
- return Task.CompletedTask;
- }
-
- public Task StopAsync()
- {
- return Task.CompletedTask;
- }
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspNetCore.Tests/ReaderExtensionsTest.cs b/Source/MQTTnet.AspNetCore.Tests/ReaderExtensionsTest.cs
deleted file mode 100644
index 5c341084d..000000000
--- a/Source/MQTTnet.AspNetCore.Tests/ReaderExtensionsTest.cs
+++ /dev/null
@@ -1,51 +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.
-
-#if NETCOREAPP3_1
-using System.Buffers;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using MQTTnet.AspNetCore;
-using MQTTnet.Formatter;
-using MQTTnet.Packets;
-
-namespace MQTTnet.AspNetCore.Tests
-{
- [TestClass]
- public sealed class ReaderExtensionsTest
- {
- [TestMethod]
- public void TestTryDeserialize()
- {
- var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
-
- var buffer = serializer.Encode(new MqttPublishPacket {Topic = "a", PayloadSegment = new byte[5]}).Join();
-
- var sequence = new ReadOnlySequence(buffer.Array, buffer.Offset, buffer.Count);
-
- var part = sequence;
- MqttPacket packet;
- var consumed = part.Start;
- var observed = part.Start;
- var result = false;
- var read = 0;
-
- part = sequence.Slice(sequence.Start, 0); // empty message should fail
- result = serializer.TryDecode(part, out packet, out consumed, out observed, out read);
- Assert.IsFalse(result);
-
- part = sequence.Slice(sequence.Start, 1); // partial fixed header should fail
- result = serializer.TryDecode(part, out packet, out consumed, out observed, out read);
- Assert.IsFalse(result);
-
- part = sequence.Slice(sequence.Start, 4); // partial body should fail
- result = serializer.TryDecode(part, out packet, out consumed, out observed, out read);
- Assert.IsFalse(result);
-
- part = sequence; // complete msg should work
- result = serializer.TryDecode(part, out packet, out consumed, out observed, out read);
- Assert.IsTrue(result);
- }
- }
-}
-#endif
\ No newline at end of file
diff --git a/Source/MQTTnet.AspTestApp/MQTTnet.AspTestApp.csproj b/Source/MQTTnet.AspTestApp/MQTTnet.AspTestApp.csproj
index 4bcabdd74..b277d7349 100644
--- a/Source/MQTTnet.AspTestApp/MQTTnet.AspTestApp.csproj
+++ b/Source/MQTTnet.AspTestApp/MQTTnet.AspTestApp.csproj
@@ -1,22 +1,28 @@
- net7.0
+ net8.0
enable
enable
false
true
false
false
- 1591;NETSDK1138
+ 1591;NETSDK1138;NU1803;NU1901;NU1902
+ true
+ all
+ true
+ latest-Recommended
-
+
+
+
-
+
diff --git a/Source/MQTTnet.AspTestApp/Pages/Index.cshtml b/Source/MQTTnet.AspTestApp/Pages/Index.cshtml
index 88d6dbe35..76cbe2c0a 100644
--- a/Source/MQTTnet.AspTestApp/Pages/Index.cshtml
+++ b/Source/MQTTnet.AspTestApp/Pages/Index.cshtml
@@ -17,7 +17,7 @@
clientId: 'mqttnet_web_client'
}
- const client = mqtt.connect('ws://localhost:5049/mqtt')
+ const client = mqtt.connect('ws://localhost:5000/mqtt')
client.on('connect', function () {
console.log('Connected')
diff --git a/Source/MQTTnet.AspTestApp/Program.cs b/Source/MQTTnet.AspTestApp/Program.cs
index 18a9ef31c..317c5f7eb 100644
--- a/Source/MQTTnet.AspTestApp/Program.cs
+++ b/Source/MQTTnet.AspTestApp/Program.cs
@@ -30,10 +30,7 @@
app.MapRazorPages();
// Setup MQTT stuff.
-app.UseEndpoints(endpoints =>
-{
- endpoints.MapMqtt("/mqtt");
-});
+app.MapMqtt("/mqtt");
app.UseMqttServer(server =>
{
@@ -42,7 +39,7 @@
_ = Task.Run(async () =>
{
var mqttApplicationMessage = new MqttApplicationMessageBuilder()
- .WithPayload($"Test application message from MQTTnet server.")
+ .WithPayload("Test application message from MQTTnet server.")
.WithTopic("message")
.Build();
diff --git a/Source/MQTTnet.AspnetCore/ApplicationBuilderExtensions.cs b/Source/MQTTnet.AspnetCore/ApplicationBuilderExtensions.cs
index d7bc3e3d7..f98983da9 100644
--- a/Source/MQTTnet.AspnetCore/ApplicationBuilderExtensions.cs
+++ b/Source/MQTTnet.AspnetCore/ApplicationBuilderExtensions.cs
@@ -7,15 +7,17 @@
using Microsoft.Extensions.DependencyInjection;
using MQTTnet.Server;
-namespace MQTTnet.AspNetCore
+namespace MQTTnet.AspNetCore;
+
+public static class ApplicationBuilderExtensions
{
- public static class ApplicationBuilderExtensions
+ [Obsolete(
+ "This class is obsolete and will be removed in a future version. The recommended alternative is to use MapMqtt inside Microsoft.AspNetCore.Builder.UseEndpoints(...).")]
+ public static IApplicationBuilder UseMqttEndpoint(this IApplicationBuilder app, string path = "/mqtt")
{
- [Obsolete("This class is obsolete and will be removed in a future version. The recommended alternative is to use MapMqtt inside Microsoft.AspNetCore.Builder.UseEndpoints(...).")]
- public static IApplicationBuilder UseMqttEndpoint(this IApplicationBuilder app, string path = "/mqtt")
- {
- app.UseWebSockets();
- app.Use(async (context, next) =>
+ app.UseWebSockets();
+ app.Use(
+ async (context, next) =>
{
if (!context.WebSockets.IsWebSocketRequest || context.Request.Path != path)
{
@@ -37,16 +39,15 @@ public static IApplicationBuilder UseMqttEndpoint(this IApplicationBuilder app,
}
});
- return app;
- }
+ return app;
+ }
- public static IApplicationBuilder UseMqttServer(this IApplicationBuilder app, Action configure)
- {
- var server = app.ApplicationServices.GetRequiredService();
+ public static IApplicationBuilder UseMqttServer(this IApplicationBuilder app, Action configure)
+ {
+ var server = app.ApplicationServices.GetRequiredService();
- configure(server);
+ configure(server);
- return app;
- }
+ return app;
}
-}
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/AspNetMqttServerOptionsBuilder.cs b/Source/MQTTnet.AspnetCore/AspNetMqttServerOptionsBuilder.cs
index a95e0017a..394483959 100644
--- a/Source/MQTTnet.AspnetCore/AspNetMqttServerOptionsBuilder.cs
+++ b/Source/MQTTnet.AspnetCore/AspNetMqttServerOptionsBuilder.cs
@@ -2,18 +2,17 @@
// 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.Server;
using System;
+using MQTTnet.Server;
+
+namespace MQTTnet.AspNetCore;
-namespace MQTTnet.AspNetCore
+public sealed class AspNetMqttServerOptionsBuilder : MqttServerOptionsBuilder
{
- public sealed class AspNetMqttServerOptionsBuilder : MqttServerOptionsBuilder
+ public AspNetMqttServerOptionsBuilder(IServiceProvider serviceProvider)
{
- public AspNetMqttServerOptionsBuilder(IServiceProvider serviceProvider)
- {
- ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
- }
-
- public IServiceProvider ServiceProvider { get; }
+ ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
}
-}
+
+ public IServiceProvider ServiceProvider { get; }
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/BufferExtensions.cs b/Source/MQTTnet.AspnetCore/BufferExtensions.cs
new file mode 100644
index 000000000..47a5c0747
--- /dev/null
+++ b/Source/MQTTnet.AspnetCore/BufferExtensions.cs
@@ -0,0 +1,21 @@
+// 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.Runtime.InteropServices;
+
+namespace MQTTnet.AspNetCore;
+
+public static class BufferExtensions
+{
+ public static ArraySegment GetArray(this ReadOnlyMemory memory)
+ {
+ if (!MemoryMarshal.TryGetArray(memory, out var result))
+ {
+ throw new InvalidOperationException("Buffer backed by array was expected");
+ }
+
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/Client/Tcp/BufferExtensions.cs b/Source/MQTTnet.AspnetCore/Client/Tcp/BufferExtensions.cs
deleted file mode 100644
index 627365920..000000000
--- a/Source/MQTTnet.AspnetCore/Client/Tcp/BufferExtensions.cs
+++ /dev/null
@@ -1,26 +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.Runtime.InteropServices;
-
-namespace MQTTnet.AspNetCore.Client.Tcp
-{
- public static class BufferExtensions
- {
- public static ArraySegment GetArray(this Memory memory)
- {
- return ((ReadOnlyMemory)memory).GetArray();
- }
-
- public static ArraySegment GetArray(this ReadOnlyMemory memory)
- {
- if (!MemoryMarshal.TryGetArray(memory, out var result))
- {
- throw new InvalidOperationException("Buffer backed by array was expected");
- }
- return result;
- }
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/Client/Tcp/DuplexPipe.cs b/Source/MQTTnet.AspnetCore/Client/Tcp/DuplexPipe.cs
deleted file mode 100644
index 84bbf9909..000000000
--- a/Source/MQTTnet.AspnetCore/Client/Tcp/DuplexPipe.cs
+++ /dev/null
@@ -1,45 +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.IO.Pipelines;
-
-namespace MQTTnet.AspNetCore.Client.Tcp
-{
- public class DuplexPipe : IDuplexPipe
- {
- public DuplexPipe(PipeReader reader, PipeWriter writer)
- {
- Input = reader;
- Output = writer;
- }
-
- public PipeReader Input { get; }
-
- public PipeWriter Output { get; }
-
- public static DuplexPipePair CreateConnectionPair(PipeOptions inputOptions, PipeOptions outputOptions)
- {
- var input = new Pipe(inputOptions);
- var output = new Pipe(outputOptions);
-
- var transportToApplication = new DuplexPipe(output.Reader, input.Writer);
- var applicationToTransport = new DuplexPipe(input.Reader, output.Writer);
-
- return new DuplexPipePair(applicationToTransport, transportToApplication);
- }
-
- // This class exists to work around issues with value tuple on .NET Framework
- public readonly struct DuplexPipePair
- {
- public IDuplexPipe Transport { get; }
- public IDuplexPipe Application { get; }
-
- public DuplexPipePair(IDuplexPipe transport, IDuplexPipe application)
- {
- Transport = transport;
- Application = application;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/Client/Tcp/SocketAwaitable.cs b/Source/MQTTnet.AspnetCore/Client/Tcp/SocketAwaitable.cs
deleted file mode 100644
index 6a53a7a1f..000000000
--- a/Source/MQTTnet.AspnetCore/Client/Tcp/SocketAwaitable.cs
+++ /dev/null
@@ -1,74 +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.Diagnostics;
-using System.IO.Pipelines;
-using System.Net.Sockets;
-using System.Runtime.CompilerServices;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MQTTnet.AspNetCore.Client.Tcp
-{
- public class SocketAwaitable : ICriticalNotifyCompletion
- {
- private static readonly Action _callbackCompleted = () => { };
-
- private readonly PipeScheduler _ioScheduler;
-
- private Action _callback;
- private int _bytesTransferred;
- private SocketError _error;
-
- public SocketAwaitable(PipeScheduler ioScheduler)
- {
- _ioScheduler = ioScheduler;
- }
-
- public bool IsCompleted => ReferenceEquals(_callback, _callbackCompleted);
-
- public SocketAwaitable GetAwaiter() => this;
-
- public int GetResult()
- {
- Debug.Assert(ReferenceEquals(_callback, _callbackCompleted));
-
- _callback = null;
-
- if (_error != SocketError.Success)
- {
- throw new SocketException((int)_error);
- }
-
- return _bytesTransferred;
- }
-
- public void OnCompleted(Action continuation)
- {
- if (ReferenceEquals(_callback, _callbackCompleted) ||
- ReferenceEquals(Interlocked.CompareExchange(ref _callback, continuation, null), _callbackCompleted))
- {
- Task.Run(continuation);
- }
- }
-
- public void UnsafeOnCompleted(Action continuation)
- {
- OnCompleted(continuation);
- }
-
- public void Complete(int bytesTransferred, SocketError socketError)
- {
- _error = socketError;
- _bytesTransferred = bytesTransferred;
- var continuation = Interlocked.Exchange(ref _callback, _callbackCompleted);
-
- if (continuation != null)
- {
- _ioScheduler.Schedule(state => ((Action)state)(), continuation);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/Client/Tcp/SocketConnection.cs b/Source/MQTTnet.AspnetCore/Client/Tcp/SocketConnection.cs
deleted file mode 100644
index 8fb199147..000000000
--- a/Source/MQTTnet.AspnetCore/Client/Tcp/SocketConnection.cs
+++ /dev/null
@@ -1,273 +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 Microsoft.AspNetCore.Connections;
-using Microsoft.AspNetCore.Http.Features;
-using MQTTnet.Exceptions;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.IO.Pipelines;
-using System.Net;
-using System.Net.Sockets;
-using System.Threading.Tasks;
-
-namespace MQTTnet.AspNetCore.Client.Tcp
-{
- public sealed class SocketConnection : ConnectionContext
- {
- volatile bool _aborted;
- readonly EndPoint _endPoint;
- SocketSender _sender;
- SocketReceiver _receiver;
-
- Socket _socket;
- IDuplexPipe _application;
-
- public bool IsConnected { get; private set; }
- public override string ConnectionId { get; set; }
- public override IFeatureCollection Features { get; }
- public override IDictionary Items { get; set; }
- public override IDuplexPipe Transport { get; set; }
-
- public SocketConnection(EndPoint endPoint)
- {
- _endPoint = endPoint;
- }
-
- public SocketConnection(Socket socket)
- {
- _socket = socket;
- _endPoint = socket.RemoteEndPoint;
-
- _sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
- _receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
- }
-#if NETCOREAPP3_1_OR_GREATER
- public override ValueTask DisposeAsync()
-#else
- public Task DisposeAsync()
-#endif
- {
- IsConnected = false;
-
- Transport?.Output.Complete();
- Transport?.Input.Complete();
-
- _socket?.Dispose();
-
-#if NETCOREAPP3_1_OR_GREATER
-
- return base.DisposeAsync();
- }
-#else
-
- return Task.CompletedTask;
- }
-#endif
-
- public async Task StartAsync()
- {
- if (_socket == null)
- {
- _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- _sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
- _receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
- await _socket.ConnectAsync(_endPoint);
- }
-
- var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
-
- Transport = pair.Transport;
- _application = pair.Application;
-
- _ = ExecuteAsync();
-
- IsConnected = true;
- }
-
- async Task ExecuteAsync()
- {
- Exception sendError = null;
- try
- {
- // Spawn send and receive logic
- var receiveTask = DoReceive();
- var sendTask = DoSend();
-
- // If the sending task completes then close the receive
- // We don't need to do this in the other direction because the kestrel
- // will trigger the output closing once the input is complete.
- if (await Task.WhenAny(receiveTask, sendTask) == sendTask)
- {
- // Tell the reader it's being aborted
- _socket.Dispose();
- }
-
- // Now wait for both to complete
- await receiveTask;
- sendError = await sendTask;
-
- // Dispose the socket(should noop if already called)
- _socket.Dispose();
- }
- catch (Exception ex)
- {
- Console.WriteLine($"Unexpected exception in {nameof(SocketConnection)}.{nameof(StartAsync)}: " + ex);
- }
- finally
- {
- // Complete the output after disposing the socket
- _application.Input.Complete(sendError);
- }
- }
-
- async Task DoReceive()
- {
- Exception error = null;
-
- try
- {
- await ProcessReceives();
- }
- catch (SocketException ex) when (ex.SocketErrorCode == SocketError.ConnectionReset)
- {
- error = new MqttCommunicationException(ex);
- }
- catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted ||
- ex.SocketErrorCode == SocketError.ConnectionAborted ||
- ex.SocketErrorCode == SocketError.Interrupted ||
- ex.SocketErrorCode == SocketError.InvalidArgument)
- {
- if (!_aborted)
- {
- // Calling Dispose after ReceiveAsync can cause an "InvalidArgument" error on *nix.
- error = ConnectionAborted();
- }
- }
- catch (ObjectDisposedException)
- {
- if (!_aborted)
- {
- error = ConnectionAborted();
- }
- }
- catch (IOException ex)
- {
- error = ex;
- }
- catch (Exception ex)
- {
- error = new IOException(ex.Message, ex);
- }
- finally
- {
- if (_aborted)
- {
- error = error ?? ConnectionAborted();
- }
-
- _application.Output.Complete(error);
- }
- }
-
- async Task ProcessReceives()
- {
- while (true)
- {
- // Ensure we have some reasonable amount of buffer space
- var buffer = _application.Output.GetMemory();
-
- var bytesReceived = await _receiver.ReceiveAsync(buffer);
-
- if (bytesReceived == 0)
- {
- // FIN
- break;
- }
-
- _application.Output.Advance(bytesReceived);
-
- var flushTask = _application.Output.FlushAsync();
-
- if (!flushTask.IsCompleted)
- {
- await flushTask;
- }
-
- var result = flushTask.GetAwaiter().GetResult();
- if (result.IsCompleted)
- {
- // Pipe consumer is shut down, do we stop writing
- break;
- }
- }
- }
-
- Exception ConnectionAborted()
- {
- return new MqttCommunicationException("Connection Aborted");
- }
-
- async Task DoSend()
- {
- Exception error = null;
-
- try
- {
- await ProcessSends();
- }
- catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted)
- {
- }
- catch (ObjectDisposedException)
- {
- }
- catch (IOException ex)
- {
- error = ex;
- }
- catch (Exception ex)
- {
- error = new IOException(ex.Message, ex);
- }
- finally
- {
- _aborted = true;
- _socket.Shutdown(SocketShutdown.Both);
- }
-
- return error;
- }
-
- async Task ProcessSends()
- {
- while (true)
- {
- // Wait for data to write from the pipe producer
- var result = await _application.Input.ReadAsync();
- var buffer = result.Buffer;
-
- if (result.IsCanceled)
- {
- break;
- }
-
- var end = buffer.End;
- var isCompleted = result.IsCompleted;
- if (!buffer.IsEmpty)
- {
- await _sender.SendAsync(buffer);
- }
-
- _application.Input.AdvanceTo(end);
-
- if (isCompleted)
- {
- break;
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/Client/Tcp/SocketReceiver.cs b/Source/MQTTnet.AspnetCore/Client/Tcp/SocketReceiver.cs
deleted file mode 100644
index a10708ec7..000000000
--- a/Source/MQTTnet.AspnetCore/Client/Tcp/SocketReceiver.cs
+++ /dev/null
@@ -1,41 +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.IO.Pipelines;
-using System.Net.Sockets;
-
-namespace MQTTnet.AspNetCore.Client.Tcp
-{
- public class SocketReceiver
- {
- private readonly Socket _socket;
- private readonly SocketAsyncEventArgs _eventArgs = new SocketAsyncEventArgs();
- private readonly SocketAwaitable _awaitable;
-
- public SocketReceiver(Socket socket, PipeScheduler scheduler)
- {
- _socket = socket;
- _awaitable = new SocketAwaitable(scheduler);
- _eventArgs.UserToken = _awaitable;
- _eventArgs.Completed += (_, e) => ((SocketAwaitable)e.UserToken).Complete(e.BytesTransferred, e.SocketError);
- }
-
- public SocketAwaitable ReceiveAsync(Memory buffer)
- {
-#if NETCOREAPP2_1
- _eventArgs.SetBuffer(buffer);
-#else
- var segment = buffer.GetArray();
- _eventArgs.SetBuffer(segment.Array, segment.Offset, segment.Count);
-#endif
- if (!_socket.ReceiveAsync(_eventArgs))
- {
- _awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
- }
-
- return _awaitable;
- }
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/Client/Tcp/SocketSender.cs b/Source/MQTTnet.AspnetCore/Client/Tcp/SocketSender.cs
deleted file mode 100644
index d4a5dcc75..000000000
--- a/Source/MQTTnet.AspnetCore/Client/Tcp/SocketSender.cs
+++ /dev/null
@@ -1,105 +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.Buffers;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO.Pipelines;
-using System.Net.Sockets;
-
-#if NETCOREAPP2_1
-using System.Runtime.InteropServices;
-#endif
-
-namespace MQTTnet.AspNetCore.Client.Tcp
-{
- public class SocketSender
- {
- private readonly Socket _socket;
- private readonly SocketAsyncEventArgs _eventArgs = new SocketAsyncEventArgs();
- private readonly SocketAwaitable _awaitable;
-
- private List> _bufferList;
-
- public SocketSender(Socket socket, PipeScheduler scheduler)
- {
- _socket = socket;
- _awaitable = new SocketAwaitable(scheduler);
- _eventArgs.UserToken = _awaitable;
- _eventArgs.Completed += (_, e) => ((SocketAwaitable)e.UserToken).Complete(e.BytesTransferred, e.SocketError);
- }
-
- public SocketAwaitable SendAsync(in ReadOnlySequence buffers)
- {
- if (buffers.IsSingleSegment)
- {
- return SendAsync(buffers.First);
- }
-
-#if NETCOREAPP2_1
- if (!_eventArgs.MemoryBuffer.Equals(Memory.Empty))
-#else
- if (_eventArgs.Buffer != null)
-#endif
- {
- _eventArgs.SetBuffer(null, 0, 0);
- }
-
- _eventArgs.BufferList = GetBufferList(buffers);
-
- if (!_socket.SendAsync(_eventArgs))
- {
- _awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
- }
-
- return _awaitable;
- }
-
- private SocketAwaitable SendAsync(ReadOnlyMemory memory)
- {
- // The BufferList getter is much less expensive then the setter.
- if (_eventArgs.BufferList != null)
- {
- _eventArgs.BufferList = null;
- }
-
-#if NETCOREAPP2_1
- _eventArgs.SetBuffer(MemoryMarshal.AsMemory(memory));
-#else
- var segment = memory.GetArray();
- _eventArgs.SetBuffer(segment.Array, segment.Offset, segment.Count);
-#endif
- if (!_socket.SendAsync(_eventArgs))
- {
- _awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
- }
-
- return _awaitable;
- }
-
- private List> GetBufferList(in ReadOnlySequence buffer)
- {
- Debug.Assert(!buffer.IsEmpty);
- Debug.Assert(!buffer.IsSingleSegment);
-
- if (_bufferList == null)
- {
- _bufferList = new List>();
- }
- else
- {
- // Buffers are pooled, so it's OK to root them until the next multi-buffer write.
- _bufferList.Clear();
- }
-
- foreach (var b in buffer)
- {
- _bufferList.Add(b.GetArray());
- }
-
- return _bufferList;
- }
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/ConnectionRouteBuilderExtensions.cs b/Source/MQTTnet.AspnetCore/ConnectionRouteBuilderExtensions.cs
deleted file mode 100644
index cbbe9f3b9..000000000
--- a/Source/MQTTnet.AspnetCore/ConnectionRouteBuilderExtensions.cs
+++ /dev/null
@@ -1,29 +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 Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Http.Connections;
-
-#if NETCOREAPP3_1
-using System;
-#endif
-
-namespace MQTTnet.AspNetCore
-{
- public static class ConnectionRouteBuilderExtensions
- {
-#if NETCOREAPP3_1
- [Obsolete("This class is obsolete and will be removed in a future version. The recommended alternative is to use MapMqtt inside Microsoft.AspNetCore.Builder.UseEndpoints(...).")]
-#endif
-#if NETCOREAPP3_1 || NETCOREAPP2_1 || NETSTANDARD
- public static void MapMqtt(this ConnectionsRouteBuilder connection, PathString path)
- {
- connection.MapConnectionHandler(path, options =>
- {
- options.WebSockets.SubProtocolSelector = MqttSubProtocolSelector.SelectSubProtocol;
- });
- }
-#endif
- }
-}
diff --git a/Source/MQTTnet.AspnetCore/DuplexPipe.cs b/Source/MQTTnet.AspnetCore/DuplexPipe.cs
new file mode 100644
index 000000000..35075e800
--- /dev/null
+++ b/Source/MQTTnet.AspnetCore/DuplexPipe.cs
@@ -0,0 +1,44 @@
+// 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.IO.Pipelines;
+
+namespace MQTTnet.AspNetCore;
+
+public class DuplexPipe : IDuplexPipe
+{
+ public DuplexPipe(PipeReader reader, PipeWriter writer)
+ {
+ Input = reader;
+ Output = writer;
+ }
+
+ public PipeReader Input { get; }
+
+ public PipeWriter Output { get; }
+
+ public static DuplexPipePair CreateConnectionPair(PipeOptions inputOptions, PipeOptions outputOptions)
+ {
+ var input = new Pipe(inputOptions);
+ var output = new Pipe(outputOptions);
+
+ var transportToApplication = new DuplexPipe(output.Reader, input.Writer);
+ var applicationToTransport = new DuplexPipe(input.Reader, output.Writer);
+
+ return new DuplexPipePair(applicationToTransport, transportToApplication);
+ }
+
+ // This class exists to work around issues with value tuple on .NET Framework
+ public readonly struct DuplexPipePair
+ {
+ public IDuplexPipe Transport { get; }
+ public IDuplexPipe Application { get; }
+
+ public DuplexPipePair(IDuplexPipe transport, IDuplexPipe application)
+ {
+ Transport = transport;
+ Application = application;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/EndpointRouterExtensions.cs b/Source/MQTTnet.AspnetCore/EndpointRouterExtensions.cs
index 5c49cd4c9..6640e07e6 100644
--- a/Source/MQTTnet.AspnetCore/EndpointRouterExtensions.cs
+++ b/Source/MQTTnet.AspnetCore/EndpointRouterExtensions.cs
@@ -2,9 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-
-#if NETCOREAPP3_1_OR_GREATER
-
+using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
@@ -12,8 +10,13 @@ namespace MQTTnet.AspNetCore
{
public static class EndpointRouterExtensions
{
- public static void MapMqtt(this IEndpointRouteBuilder endpoints, string pattern)
+ public static void MapMqtt(this IEndpointRouteBuilder endpoints, string pattern)
{
+ if (endpoints == null)
+ {
+ throw new ArgumentNullException(nameof(endpoints));
+ }
+
endpoints.MapConnectionHandler(pattern, options =>
{
options.WebSockets.SubProtocolSelector = MqttSubProtocolSelector.SelectSubProtocol;
@@ -22,4 +25,3 @@ public static void MapMqtt(this IEndpointRouteBuilder endpoints, string pattern)
}
}
-#endif
diff --git a/Source/MQTTnet.AspnetCore/MQTTnet.AspNetCore.csproj b/Source/MQTTnet.AspnetCore/MQTTnet.AspNetCore.csproj
index 6a30ffcc1..7bfd85cba 100644
--- a/Source/MQTTnet.AspnetCore/MQTTnet.AspNetCore.csproj
+++ b/Source/MQTTnet.AspnetCore/MQTTnet.AspNetCore.csproj
@@ -1,8 +1,7 @@
- netstandard2.0;netcoreapp2.1;netcoreapp3.1;net5.0;net6.0;net7.0
-
+ net8.0
MQTTnet.AspNetCore
MQTTnet.AspNetCore
True
@@ -32,8 +31,11 @@
true
true
true
- 1591;NETSDK1138
- 7.3
+ 1591;NETSDK1138;NU1803;NU1901;NU1902
+ true
+ all
+ true
+ latest-Recommended
@@ -44,23 +46,16 @@
-
+
+
-
- RELEASE;NETSTANDARD2_0
+
-
-
-
-
-
-
-
-
-
+
+
diff --git a/Source/MQTTnet.AspnetCore/Client/MqttClientConnectionContextFactory.cs b/Source/MQTTnet.AspnetCore/MqttClientConnectionContextFactory.cs
similarity index 89%
rename from Source/MQTTnet.AspnetCore/Client/MqttClientConnectionContextFactory.cs
rename to Source/MQTTnet.AspnetCore/MqttClientConnectionContextFactory.cs
index 7afd3e215..0ddbbd8f1 100644
--- a/Source/MQTTnet.AspnetCore/Client/MqttClientConnectionContextFactory.cs
+++ b/Source/MQTTnet.AspnetCore/MqttClientConnectionContextFactory.cs
@@ -2,14 +2,12 @@
// 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 MQTTnet.Adapter;
-using MQTTnet.AspNetCore.Client.Tcp;
+using MQTTnet.Diagnostics.Logger;
using MQTTnet.Formatter;
-using System;
-using MQTTnet.Client;
-using MQTTnet.Diagnostics;
-namespace MQTTnet.AspNetCore.Client
+namespace MQTTnet.AspNetCore
{
public sealed class MqttClientConnectionContextFactory : IMqttClientAdapterFactory
{
@@ -22,7 +20,7 @@ public IMqttChannelAdapter CreateClientAdapter(MqttClientOptions options, MqttPa
case MqttClientTcpOptions tcpOptions:
{
var tcpConnection = new SocketConnection(tcpOptions.RemoteEndpoint);
-
+
var formatter = new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(4096, 65535));
return new MqttConnectionContext(formatter, tcpConnection);
}
diff --git a/Source/MQTTnet.AspnetCore/MqttConnectionContext.cs b/Source/MQTTnet.AspnetCore/MqttConnectionContext.cs
index da0a18240..c16e5f483 100644
--- a/Source/MQTTnet.AspnetCore/MqttConnectionContext.cs
+++ b/Source/MQTTnet.AspnetCore/MqttConnectionContext.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.Buffers;
using System.IO.Pipelines;
using System.Net;
using System.Security.Cryptography.X509Certificates;
@@ -12,230 +13,227 @@
using Microsoft.AspNetCore.Http.Connections.Features;
using Microsoft.AspNetCore.Http.Features;
using MQTTnet.Adapter;
-using MQTTnet.AspNetCore.Client.Tcp;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Internal;
using MQTTnet.Packets;
-namespace MQTTnet.AspNetCore
+namespace MQTTnet.AspNetCore;
+
+public sealed class MqttConnectionContext : IMqttChannelAdapter
{
- public sealed class MqttConnectionContext : IMqttChannelAdapter
- {
- readonly ConnectionContext _connection;
- readonly AsyncLock _writerLock = new AsyncLock();
+ readonly ConnectionContext _connection;
+ readonly AsyncLock _writerLock = new();
- PipeReader _input;
- PipeWriter _output;
+ PipeReader _input;
+ PipeWriter _output;
- public MqttConnectionContext(MqttPacketFormatterAdapter packetFormatterAdapter, ConnectionContext connection)
- {
- PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
- _connection = connection ?? throw new ArgumentNullException(nameof(connection));
+ public MqttConnectionContext(MqttPacketFormatterAdapter packetFormatterAdapter, ConnectionContext connection)
+ {
+ PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
+ _connection = connection ?? throw new ArgumentNullException(nameof(connection));
- if (!(_connection is SocketConnection tcp) || tcp.IsConnected)
- {
- _input = connection.Transport.Input;
- _output = connection.Transport.Output;
- }
+ if (!(_connection is SocketConnection tcp) || tcp.IsConnected)
+ {
+ _input = connection.Transport.Input;
+ _output = connection.Transport.Output;
}
+ }
- public long BytesReceived { get; private set; }
+ public long BytesReceived { get; private set; }
- public long BytesSent { get; private set; }
+ public long BytesSent { get; private set; }
- public X509Certificate2 ClientCertificate
+ public X509Certificate2 ClientCertificate
+ {
+ get
{
- get
+ // mqtt over tcp
+ var tlsFeature = _connection.Features.Get();
+ if (tlsFeature != null)
{
- // mqtt over tcp
- var tlsFeature = _connection.Features.Get();
- if (tlsFeature != null)
- {
- return tlsFeature.ClientCertificate;
- }
-
- // mqtt over websocket
- var httpFeature = _connection.Features.Get();
- return httpFeature?.HttpContext?.Connection.ClientCertificate;
+ return tlsFeature.ClientCertificate;
}
+
+ // mqtt over websocket
+ var httpFeature = _connection.Features.Get();
+ return httpFeature?.HttpContext?.Connection.ClientCertificate;
}
+ }
- public string Endpoint
+ public string Endpoint
+ {
+ get
{
- get
+ // mqtt over tcp
+ if (_connection.RemoteEndPoint != null)
{
- // mqtt over tcp
-#if NETCOREAPP3_1_OR_GREATER
- if (_connection.RemoteEndPoint != null)
- {
- return _connection.RemoteEndPoint.ToString();
- }
-#endif
- // mqtt over websocket
- var httpFeature = _connection.Features.Get();
- if (httpFeature?.RemoteIpAddress != null)
- {
- return new IPEndPoint(httpFeature.RemoteIpAddress, httpFeature.RemotePort).ToString();
- }
+ return _connection.RemoteEndPoint.ToString();
+ }
- return null;
+ // mqtt over websocket
+ var httpFeature = _connection.Features.Get();
+ if (httpFeature?.RemoteIpAddress != null)
+ {
+ return new IPEndPoint(httpFeature.RemoteIpAddress, httpFeature.RemotePort).ToString();
}
+
+ return null;
}
+ }
- public bool IsSecureConnection
+ public bool IsSecureConnection
+ {
+ get
{
- get
+ // mqtt over tcp
+ var tlsFeature = _connection.Features.Get();
+ if (tlsFeature != null)
{
- // mqtt over tcp
- var tlsFeature = _connection.Features.Get();
- if (tlsFeature != null)
- {
- return true;
- }
-
- // mqtt over websocket
- var httpFeature = _connection.Features.Get();
- if (httpFeature?.HttpContext != null)
- {
- return httpFeature.HttpContext.Request.IsHttps;
- }
-
- return false;
+ return true;
}
- }
-
- public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
- public async Task ConnectAsync(CancellationToken cancellationToken)
- {
- if (_connection is SocketConnection tcp && !tcp.IsConnected)
+ // mqtt over websocket
+ var httpFeature = _connection.Features.Get();
+ if (httpFeature?.HttpContext != null)
{
- await tcp.StartAsync().ConfigureAwait(false);
+ return httpFeature.HttpContext.Request.IsHttps;
}
- _input = _connection.Transport.Input;
- _output = _connection.Transport.Output;
+ return false;
}
+ }
- public Task DisconnectAsync(CancellationToken cancellationToken)
- {
- _input?.Complete();
- _output?.Complete();
-
- return Task.CompletedTask;
- }
+ public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
- public void Dispose()
+ public async Task ConnectAsync(CancellationToken cancellationToken)
+ {
+ if (_connection is SocketConnection tcp && !tcp.IsConnected)
{
- _writerLock.Dispose();
+ await tcp.StartAsync().ConfigureAwait(false);
}
- public async Task ReceivePacketAsync(CancellationToken cancellationToken)
+ _input = _connection.Transport.Input;
+ _output = _connection.Transport.Output;
+ }
+
+ public Task DisconnectAsync(CancellationToken cancellationToken)
+ {
+ _input?.Complete();
+ _output?.Complete();
+
+ return Task.CompletedTask;
+ }
+
+ public void Dispose()
+ {
+ _writerLock.Dispose();
+ }
+
+ public async Task ReceivePacketAsync(CancellationToken cancellationToken)
+ {
+ try
{
- try
+ while (!cancellationToken.IsCancellationRequested)
{
- while (!cancellationToken.IsCancellationRequested)
+ ReadResult readResult;
+ var readTask = _input.ReadAsync(cancellationToken);
+ if (readTask.IsCompleted)
{
- ReadResult readResult;
- var readTask = _input.ReadAsync(cancellationToken);
- if (readTask.IsCompleted)
- {
- readResult = readTask.Result;
- }
- else
- {
- readResult = await readTask.ConfigureAwait(false);
- }
+ readResult = readTask.Result;
+ }
+ else
+ {
+ readResult = await readTask.ConfigureAwait(false);
+ }
- var buffer = readResult.Buffer;
+ var buffer = readResult.Buffer;
- var consumed = buffer.Start;
- var observed = buffer.Start;
+ var consumed = buffer.Start;
+ var observed = buffer.Start;
- try
+ try
+ {
+ if (!buffer.IsEmpty)
{
- if (!buffer.IsEmpty)
- {
- if (PacketFormatterAdapter.TryDecode(buffer, out var packet, out consumed, out observed, out var received))
- {
- BytesReceived += received;
- return packet;
- }
- }
- else if (readResult.IsCompleted)
+ if (PacketFormatterAdapter.TryDecode(buffer, out var packet, out consumed, out observed, out var received))
{
- throw new MqttCommunicationException("Connection Aborted");
+ BytesReceived += received;
+ return packet;
}
}
- finally
+ else if (readResult.IsCompleted)
{
- // The buffer was sliced up to where it was consumed, so we can just advance to the start.
- // We mark examined as buffer.End so that if we didn't receive a full frame, we'll wait for more data
- // before yielding the read again.
- _input.AdvanceTo(consumed, observed);
+ throw new MqttCommunicationException("Connection Aborted");
}
}
+ finally
+ {
+ // The buffer was sliced up to where it was consumed, so we can just advance to the start.
+ // We mark examined as buffer.End so that if we didn't receive a full frame, we'll wait for more data
+ // before yielding the read again.
+ _input.AdvanceTo(consumed, observed);
+ }
}
- catch (Exception exception)
- {
- // completing the channel makes sure that there is no more data read after a protocol error
- _input?.Complete(exception);
- _output?.Complete(exception);
-
- throw;
- }
-
- cancellationToken.ThrowIfCancellationRequested();
- return null;
}
-
- public void ResetStatistics()
+ catch (Exception exception)
{
- BytesReceived = 0;
- BytesSent = 0;
+ // completing the channel makes sure that there is no more data read after a protocol error
+ _input?.Complete(exception);
+ _output?.Complete(exception);
+
+ throw;
}
- public async Task SendPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
+ cancellationToken.ThrowIfCancellationRequested();
+ return null;
+ }
+
+ public void ResetStatistics()
+ {
+ BytesReceived = 0;
+ BytesSent = 0;
+ }
+
+ public async Task SendPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
+ {
+ using (await _writerLock.EnterAsync(cancellationToken).ConfigureAwait(false))
{
- using (await _writerLock.EnterAsync(cancellationToken).ConfigureAwait(false))
+ try
{
- try
- {
- var buffer = PacketFormatterAdapter.Encode(packet);
+ var buffer = PacketFormatterAdapter.Encode(packet);
- if (buffer.Payload.Count == 0)
- {
- // zero copy
- // https://github.com/dotnet/runtime/blob/e31ddfdc4f574b26231233dc10c9a9c402f40590/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/StreamPipeWriter.cs#L279
- await _output.WriteAsync(buffer.Packet, cancellationToken).ConfigureAwait(false);
- }
- else
- {
- WritePacketBuffer(_output, buffer);
- await _output.FlushAsync(cancellationToken).ConfigureAwait(false);
- }
-
- BytesSent += buffer.Length;
+ if (buffer.Payload.Length == 0)
+ {
+ // zero copy
+ // https://github.com/dotnet/runtime/blob/e31ddfdc4f574b26231233dc10c9a9c402f40590/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/StreamPipeWriter.cs#L279
+ await _output.WriteAsync(buffer.Packet, cancellationToken).ConfigureAwait(false);
}
- finally
+ else
{
- PacketFormatterAdapter.Cleanup();
+ WritePacketBuffer(_output, buffer);
+ await _output.FlushAsync(cancellationToken).ConfigureAwait(false);
}
+
+ BytesSent += buffer.Length;
+ }
+ finally
+ {
+ PacketFormatterAdapter.Cleanup();
}
}
+ }
- static void WritePacketBuffer(PipeWriter output, MqttPacketBuffer buffer)
- {
- // copy MqttPacketBuffer's Packet and Payload to the same buffer block of PipeWriter
- // MqttPacket will be transmitted within the bounds of a WebSocket frame after PipeWriter.FlushAsync
-
- var span = output.GetSpan(buffer.Length);
+ static void WritePacketBuffer(PipeWriter output, MqttPacketBuffer buffer)
+ {
+ // copy MqttPacketBuffer's Packet and Payload to the same buffer block of PipeWriter
+ // MqttPacket will be transmitted within the bounds of a WebSocket frame after PipeWriter.FlushAsync
- buffer.Packet.AsSpan().CopyTo(span);
- buffer.Payload.AsSpan().CopyTo(span.Slice(buffer.Packet.Count));
+ var span = output.GetSpan(buffer.Length);
- output.Advance(buffer.Length);
- }
+ buffer.Packet.AsSpan().CopyTo(span);
+ int offset = buffer.Packet.Count;
+ buffer.Payload.CopyTo(destination: span.Slice(offset));
+ output.Advance(buffer.Length);
}
}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/MqttConnectionHandler.cs b/Source/MQTTnet.AspnetCore/MqttConnectionHandler.cs
index b86de3015..b4cbc42a8 100644
--- a/Source/MQTTnet.AspnetCore/MqttConnectionHandler.cs
+++ b/Source/MQTTnet.AspnetCore/MqttConnectionHandler.cs
@@ -2,57 +2,56 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Features;
using MQTTnet.Adapter;
-using MQTTnet.Server;
-using System;
-using System.Threading.Tasks;
-using MQTTnet.Diagnostics;
+using MQTTnet.Diagnostics.Logger;
using MQTTnet.Formatter;
+using MQTTnet.Server;
+
+namespace MQTTnet.AspNetCore;
-namespace MQTTnet.AspNetCore
+public sealed class MqttConnectionHandler : ConnectionHandler, IMqttServerAdapter
{
- public sealed class MqttConnectionHandler : ConnectionHandler, IMqttServerAdapter
+ MqttServerOptions _serverOptions;
+
+ public Func ClientHandler { get; set; }
+
+ public void Dispose()
{
- MqttServerOptions _serverOptions;
-
- public Func ClientHandler { get; set; }
+ }
- public override async Task OnConnectedAsync(ConnectionContext connection)
+ public override async Task OnConnectedAsync(ConnectionContext connection)
+ {
+ // required for websocket transport to work
+ var transferFormatFeature = connection.Features.Get();
+ if (transferFormatFeature != null)
{
- // required for websocket transport to work
- var transferFormatFeature = connection.Features.Get();
- if (transferFormatFeature != null)
- {
- transferFormatFeature.ActiveFormat = TransferFormat.Binary;
- }
-
- var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(_serverOptions.WriterBufferSize, _serverOptions.WriterBufferSizeMax));
- using (var adapter = new MqttConnectionContext(formatter, connection))
- {
- var clientHandler = ClientHandler;
- if (clientHandler != null)
- {
- await clientHandler(adapter).ConfigureAwait(false);
- }
- }
+ transferFormatFeature.ActiveFormat = TransferFormat.Binary;
}
- public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
+ var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(_serverOptions.WriterBufferSize, _serverOptions.WriterBufferSizeMax));
+ using (var adapter = new MqttConnectionContext(formatter, connection))
{
- _serverOptions = options;
-
- return Task.CompletedTask;
+ var clientHandler = ClientHandler;
+ if (clientHandler != null)
+ {
+ await clientHandler(adapter).ConfigureAwait(false);
+ }
}
+ }
- public Task StopAsync()
- {
- return Task.CompletedTask;
- }
+ public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
+ {
+ _serverOptions = options;
- public void Dispose()
- {
- }
+ return Task.CompletedTask;
+ }
+
+ public Task StopAsync()
+ {
+ return Task.CompletedTask;
}
-}
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/MqttHostedServer.cs b/Source/MQTTnet.AspnetCore/MqttHostedServer.cs
index b34adc3d5..4c74f6a43 100644
--- a/Source/MQTTnet.AspnetCore/MqttHostedServer.cs
+++ b/Source/MQTTnet.AspnetCore/MqttHostedServer.cs
@@ -7,60 +7,42 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
-using MQTTnet.Adapter;
-using MQTTnet.Diagnostics;
+using MQTTnet.Diagnostics.Logger;
using MQTTnet.Server;
-namespace MQTTnet.AspNetCore
+namespace MQTTnet.AspNetCore;
+
+public sealed class MqttHostedServer : MqttServer, IHostedService
{
- public sealed class MqttHostedServer : MqttServer, IHostedService
- {
- readonly MqttFactory _mqttFactory;
-#if NETCOREAPP3_1_OR_GREATER
- readonly IHostApplicationLifetime _hostApplicationLifetime;
- public MqttHostedServer(IHostApplicationLifetime hostApplicationLifetime, MqttFactory mqttFactory,
- MqttServerOptions options, IEnumerable adapters, IMqttNetLogger logger) : base(
- options,
- adapters,
- logger)
- {
- _mqttFactory = mqttFactory ?? throw new ArgumentNullException(nameof(mqttFactory));
- _hostApplicationLifetime = hostApplicationLifetime;
- }
-#else
- public MqttHostedServer(MqttFactory mqttFactory,
- MqttServerOptions options, IEnumerable adapters, IMqttNetLogger logger) : base(
- options,
- adapters,
- logger)
- {
- _mqttFactory = mqttFactory ?? throw new ArgumentNullException(nameof(mqttFactory));
- }
-#endif
+ readonly IHostApplicationLifetime _hostApplicationLifetime;
+ readonly MqttServerFactory _mqttFactory;
+ public MqttHostedServer(
+ IHostApplicationLifetime hostApplicationLifetime,
+ MqttServerFactory mqttFactory,
+ MqttServerOptions options,
+ IEnumerable adapters,
+ IMqttNetLogger logger) : base(options, adapters, logger)
+ {
+ _mqttFactory = mqttFactory ?? throw new ArgumentNullException(nameof(mqttFactory));
+ _hostApplicationLifetime = hostApplicationLifetime;
+ }
- public async Task StartAsync(CancellationToken cancellationToken)
- {
- // The yield makes sure that the hosted service is considered up and running.
- await Task.Yield();
-#if NETCOREAPP3_1_OR_GREATER
- _hostApplicationLifetime.ApplicationStarted.Register(OnStarted);
-#else
- _ = StartAsync();
-#endif
+ public async Task StartAsync(CancellationToken cancellationToken)
+ {
+ // The yield makes sure that the hosted service is considered up and running.
+ await Task.Yield();
- }
+ _hostApplicationLifetime.ApplicationStarted.Register(OnStarted);
+ }
- public Task StopAsync(CancellationToken cancellationToken)
- {
- return StopAsync(_mqttFactory.CreateMqttServerStopOptionsBuilder().Build());
- }
+ public Task StopAsync(CancellationToken cancellationToken)
+ {
+ return StopAsync(_mqttFactory.CreateMqttServerStopOptionsBuilder().Build());
+ }
-#if NETCOREAPP3_1_OR_GREATER
- private void OnStarted()
- {
- _ = StartAsync();
- }
-#endif
+ void OnStarted()
+ {
+ _ = StartAsync();
}
}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/MqttSubProtocolSelector.cs b/Source/MQTTnet.AspnetCore/MqttSubProtocolSelector.cs
index 6f318b1bb..5030f2d9f 100644
--- a/Source/MQTTnet.AspnetCore/MqttSubProtocolSelector.cs
+++ b/Source/MQTTnet.AspnetCore/MqttSubProtocolSelector.cs
@@ -7,31 +7,34 @@
using System.Linq;
using Microsoft.AspNetCore.Http;
-namespace MQTTnet.AspNetCore
+namespace MQTTnet.AspNetCore;
+
+public static class MqttSubProtocolSelector
{
- public static class MqttSubProtocolSelector
+ public static string SelectSubProtocol(HttpRequest request)
{
- public static string SelectSubProtocol(HttpRequest request)
+ if (request == null)
{
- if (request == null) throw new ArgumentNullException(nameof(request));
-
- string subProtocol = null;
- if (request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
- {
- subProtocol = SelectSubProtocol(requestedSubProtocolValues);
- }
-
- return subProtocol;
+ throw new ArgumentNullException(nameof(request));
}
-
- public static string SelectSubProtocol(IList requestedSubProtocolValues)
+
+ string subProtocol = null;
+ if (request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
{
- if (requestedSubProtocolValues == null) throw new ArgumentNullException(nameof(requestedSubProtocolValues));
+ subProtocol = SelectSubProtocol(requestedSubProtocolValues);
+ }
- // 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 subProtocol;
+ }
+
+ public static string SelectSubProtocol(IList requestedSubProtocolValues)
+ {
+ if (requestedSubProtocolValues == null)
+ {
+ throw new ArgumentNullException(nameof(requestedSubProtocolValues));
}
+
+ // 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"));
}
-}
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/MqttWebSocketServerAdapter.cs b/Source/MQTTnet.AspnetCore/MqttWebSocketServerAdapter.cs
index 183fbfb69..9b364231d 100644
--- a/Source/MQTTnet.AspnetCore/MqttWebSocketServerAdapter.cs
+++ b/Source/MQTTnet.AspnetCore/MqttWebSocketServerAdapter.cs
@@ -7,64 +7,63 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using MQTTnet.Adapter;
-using MQTTnet.Diagnostics;
+using MQTTnet.Diagnostics.Logger;
using MQTTnet.Formatter;
using MQTTnet.Implementations;
using MQTTnet.Server;
-namespace MQTTnet.AspNetCore
+namespace MQTTnet.AspNetCore;
+
+public sealed class MqttWebSocketServerAdapter : IMqttServerAdapter
{
- public sealed class MqttWebSocketServerAdapter : IMqttServerAdapter
- {
- IMqttNetLogger _logger = MqttNetNullLogger.Instance;
+ IMqttNetLogger _logger = MqttNetNullLogger.Instance;
+
+ public Func ClientHandler { get; set; }
- public Func ClientHandler { get; set; }
+ public void Dispose()
+ {
+ }
- public void Dispose()
+ public async Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
+ {
+ if (webSocket == null)
{
+ throw new ArgumentNullException(nameof(webSocket));
}
- public async Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
- {
- if (webSocket == null)
- {
- throw new ArgumentNullException(nameof(webSocket));
- }
+ var endpoint = $"{httpContext.Connection.RemoteIpAddress}:{httpContext.Connection.RemotePort}";
- var endpoint = $"{httpContext.Connection.RemoteIpAddress}:{httpContext.Connection.RemotePort}";
+ var clientCertificate = await httpContext.Connection.GetClientCertificateAsync().ConfigureAwait(false);
+ try
+ {
+ var isSecureConnection = clientCertificate != null;
- var clientCertificate = await httpContext.Connection.GetClientCertificateAsync().ConfigureAwait(false);
- try
+ var clientHandler = ClientHandler;
+ if (clientHandler != null)
{
- var isSecureConnection = clientCertificate != null;
+ var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535));
+ var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection, clientCertificate);
- var clientHandler = ClientHandler;
- if (clientHandler != null)
+ using (var channelAdapter = new MqttChannelAdapter(channel, formatter, _logger))
{
- var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535));
- var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection, clientCertificate);
-
- using (var channelAdapter = new MqttChannelAdapter(channel, formatter, _logger))
- {
- await clientHandler(channelAdapter).ConfigureAwait(false);
- }
+ await clientHandler(channelAdapter).ConfigureAwait(false);
}
}
- finally
- {
- clientCertificate?.Dispose();
- }
}
-
- public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
+ finally
{
- _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- return Task.CompletedTask;
+ clientCertificate?.Dispose();
}
+ }
- public Task StopAsync()
- {
- return Task.CompletedTask;
- }
+ public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
+ {
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ return Task.CompletedTask;
+ }
+
+ public Task StopAsync()
+ {
+ return Task.CompletedTask;
}
}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/ReaderExtensions.cs b/Source/MQTTnet.AspnetCore/ReaderExtensions.cs
index c7d289fd6..d120062ba 100644
--- a/Source/MQTTnet.AspnetCore/ReaderExtensions.cs
+++ b/Source/MQTTnet.AspnetCore/ReaderExtensions.cs
@@ -2,139 +2,137 @@
// 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.Buffers;
+using System.Runtime.InteropServices;
using MQTTnet.Adapter;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Packets;
-using System;
-using System.Buffers;
-using System.Linq;
-using System.Runtime.InteropServices;
-namespace MQTTnet.AspNetCore
+namespace MQTTnet.AspNetCore;
+
+public static class ReaderExtensions
{
- public static class ReaderExtensions
+ public static bool TryDecode(
+ this MqttPacketFormatterAdapter formatter,
+ in ReadOnlySequence input,
+ out MqttPacket packet,
+ out SequencePosition consumed,
+ out SequencePosition observed,
+ out int bytesRead)
{
- public static bool TryDecode(this MqttPacketFormatterAdapter formatter,
- in ReadOnlySequence input,
- out MqttPacket packet,
- out SequencePosition consumed,
- out SequencePosition observed,
- out int bytesRead)
+ if (formatter == null)
{
- if (formatter == null) throw new ArgumentNullException(nameof(formatter));
+ throw new ArgumentNullException(nameof(formatter));
+ }
- packet = null;
- consumed = input.Start;
- observed = input.End;
- bytesRead = 0;
- var copy = input;
+ packet = null;
+ consumed = input.Start;
+ observed = input.End;
+ bytesRead = 0;
+ var copy = input;
- if (copy.Length < 2)
- {
- return false;
- }
+ if (copy.Length < 2)
+ {
+ return false;
+ }
- if (!TryReadBodyLength(ref copy, out var headerLength, out var bodyLength))
- {
- return false;
- }
+ if (!TryReadBodyLength(ref copy, out var headerLength, out var bodyLength))
+ {
+ return false;
+ }
- var fixedHeader = copy.First.Span[0];
- copy = copy.Slice(headerLength);
- if (copy.Length < bodyLength)
- {
- return false;
- }
+ var fixedHeader = copy.First.Span[0];
+ copy = copy.Slice(headerLength);
+ if (copy.Length < bodyLength)
+ {
+ return false;
+ }
- var bodySlice = copy.Slice(0, bodyLength);
- var bodySegment = GetArraySegment(ref bodySlice);
-
- var receivedMqttPacket = new ReceivedMqttPacket(fixedHeader, bodySegment, headerLength + bodyLength);
- if (formatter.ProtocolVersion == MqttProtocolVersion.Unknown)
- {
- formatter.DetectProtocolVersion(receivedMqttPacket);
- }
+ var bodySlice = copy.Slice(0, bodyLength);
+ var bodySegment = GetArraySegment(ref bodySlice);
- packet = formatter.Decode(receivedMqttPacket);
- consumed = bodySlice.End;
- observed = bodySlice.End;
- bytesRead = headerLength + bodyLength;
- return true;
+ var receivedMqttPacket = new ReceivedMqttPacket(fixedHeader, bodySegment, headerLength + bodyLength);
+ if (formatter.ProtocolVersion == MqttProtocolVersion.Unknown)
+ {
+ formatter.DetectProtocolVersion(receivedMqttPacket);
}
- static ArraySegment GetArraySegment(ref ReadOnlySequence input)
+ packet = formatter.Decode(receivedMqttPacket);
+ consumed = bodySlice.End;
+ observed = bodySlice.End;
+ bytesRead = headerLength + bodyLength;
+ return true;
+ }
+
+ static ArraySegment GetArraySegment(ref ReadOnlySequence input)
+ {
+ if (input.IsSingleSegment && MemoryMarshal.TryGetArray(input.First, out var segment))
{
- if (input.IsSingleSegment && MemoryMarshal.TryGetArray(input.First, out var segment))
- {
- return segment;
- }
+ return segment;
+ }
+
+ // Should be rare
+ var array = input.ToArray();
+ return new ArraySegment(array);
+ }
+
- // Should be rare
- var array = input.ToArray();
- return new ArraySegment(array);
+ static void ThrowProtocolViolationException(ReadOnlySpan valueSpan, int index)
+ {
+ throw new MqttProtocolViolationException($"Remaining length is invalid (Data={string.Join(",", valueSpan.Slice(1, index).ToArray())}).");
+ }
+
+ static bool TryReadBodyLength(ref ReadOnlySequence input, out int headerLength, out int bodyLength)
+ {
+ var valueSequence = input.Slice(0, Math.Min(5, input.Length));
+ if (valueSequence.IsSingleSegment)
+ {
+ var valueSpan = valueSequence.FirstSpan;
+
+ return TryReadBodyLength(valueSpan, out headerLength, out bodyLength);
}
+ else
+ {
+ Span valueSpan = stackalloc byte[8];
+ valueSequence.CopyTo(valueSpan);
+ valueSpan = valueSpan.Slice(0, (int)valueSequence.Length);
+ return TryReadBodyLength(valueSpan, out headerLength, out bodyLength);
+ }
+ }
- static bool TryReadBodyLength(ref ReadOnlySequence input, out int headerLength, out int bodyLength)
+ static bool TryReadBodyLength(ReadOnlySpan span, out int headerLength, out int bodyLength)
+ {
+ // Alorithm taken from https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html.
+ var multiplier = 1;
+ var value = 0;
+ byte encodedByte;
+ var index = 1;
+ headerLength = 0;
+ bodyLength = 0;
+
+ do
{
- var valueSequence = input.Slice(0, Math.Min(5, input.Length));
- if (valueSequence.IsSingleSegment)
+ if (index == span.Length)
{
- var valueSpan =
-#if NET5_0_OR_GREATER
- valueSequence.FirstSpan;
-#else
- valueSequence.First.Span;
-#endif
- return TryReadBodyLength(valueSpan, out headerLength, out bodyLength);
- }
- else
- {
- Span valueSpan = stackalloc byte[8];
- valueSequence.CopyTo(valueSpan);
- valueSpan = valueSpan.Slice(0, (int)valueSequence.Length);
- return TryReadBodyLength(valueSpan, out headerLength, out bodyLength);
+ return false;
}
- }
- static bool TryReadBodyLength(ReadOnlySpan span, out int headerLength, out int bodyLength)
- {
- // Alorithm taken from https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html.
- var multiplier = 1;
- var value = 0;
- byte encodedByte;
- var index = 1;
- headerLength = 0;
- bodyLength = 0;
-
- do
+ encodedByte = span[index];
+ index++;
+
+ value += (byte)(encodedByte & 127) * multiplier;
+ if (multiplier > 128 * 128 * 128)
{
- if (index == span.Length)
- {
- return false;
- }
-
- encodedByte = span[index];
- index++;
-
- value += (byte)(encodedByte & 127) * multiplier;
- if (multiplier > 128 * 128 * 128)
- {
- ThrowProtocolViolationException(span, index);
- }
-
- multiplier *= 128;
- } while ((encodedByte & 128) != 0);
-
- headerLength = index;
- bodyLength = value;
- return true;
- }
+ ThrowProtocolViolationException(span, index);
+ }
+ multiplier *= 128;
+ } while ((encodedByte & 128) != 0);
- static void ThrowProtocolViolationException(ReadOnlySpan valueSpan, int index)
- {
- throw new MqttProtocolViolationException($"Remaining length is invalid (Data={string.Join(",", valueSpan.Slice(1, index).ToArray())}).");
- }
+ headerLength = index;
+ bodyLength = value;
+ return true;
}
-}
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/ServiceCollectionExtensions.cs b/Source/MQTTnet.AspnetCore/ServiceCollectionExtensions.cs
index 562ccd3cb..d662187f3 100644
--- a/Source/MQTTnet.AspnetCore/ServiceCollectionExtensions.cs
+++ b/Source/MQTTnet.AspnetCore/ServiceCollectionExtensions.cs
@@ -6,125 +6,123 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
-using MQTTnet.Adapter;
-using MQTTnet.Diagnostics;
-using MQTTnet.Implementations;
+using MQTTnet.Diagnostics.Logger;
using MQTTnet.Server;
+using MQTTnet.Server.Internal.Adapter;
-namespace MQTTnet.AspNetCore
+namespace MQTTnet.AspNetCore;
+
+public static class ServiceCollectionExtensions
{
- 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)
{
- if (services == null)
- {
- throw new ArgumentNullException(nameof(services));
- }
+ throw new ArgumentNullException(nameof(services));
+ }
- if (options == null)
- {
- throw new ArgumentNullException(nameof(options));
- }
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
- services.AddSingleton(options);
- services.AddHostedMqttServer();
+ services.AddSingleton(options);
+ services.AddHostedMqttServer();
- return services;
- }
+ return services;
+ }
- public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, Action configure)
+ public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, Action configure)
+ {
+ if (services == null)
{
- if (services == null)
- {
- throw new ArgumentNullException(nameof(services));
- }
+ throw new ArgumentNullException(nameof(services));
+ }
- var serverOptionsBuilder = new MqttServerOptionsBuilder();
+ var serverOptionsBuilder = new MqttServerOptionsBuilder();
- configure?.Invoke(serverOptionsBuilder);
+ configure?.Invoke(serverOptionsBuilder);
- var options = serverOptionsBuilder.Build();
+ var options = serverOptionsBuilder.Build();
- return AddHostedMqttServer(services, options);
- }
+ return AddHostedMqttServer(services, options);
+ }
- public static void AddHostedMqttServer(this IServiceCollection services)
- {
- // The user may have these services already registered.
- services.TryAddSingleton(MqttNetNullLogger.Instance);
- services.TryAddSingleton(new MqttFactory());
+ public static void AddHostedMqttServer(this IServiceCollection services)
+ {
+ // The user may have these services already registered.
+ services.TryAddSingleton(MqttNetNullLogger.Instance);
+ services.TryAddSingleton(new MqttServerFactory());
- services.AddSingleton();
- services.AddSingleton(s => s.GetService());
- services.AddSingleton(s => s.GetService());
- }
+ services.AddSingleton();
+ services.AddSingleton(s => s.GetService());
+ services.AddSingleton(s => s.GetService());
+ }
- public static IServiceCollection AddHostedMqttServerWithServices(this IServiceCollection services, Action configure)
+ public static IServiceCollection AddHostedMqttServerWithServices(this IServiceCollection services, Action configure)
+ {
+ if (services == null)
{
- if (services == null)
- {
- throw new ArgumentNullException(nameof(services));
- }
+ throw new ArgumentNullException(nameof(services));
+ }
- services.AddSingleton(
- s =>
- {
- var builder = new AspNetMqttServerOptionsBuilder(s);
- configure(builder);
- return builder.Build();
- });
+ services.AddSingleton(
+ s =>
+ {
+ var builder = new AspNetMqttServerOptionsBuilder(s);
+ configure(builder);
+ return builder.Build();
+ });
- services.AddHostedMqttServer();
+ services.AddHostedMqttServer();
- return services;
- }
+ return services;
+ }
- public static IServiceCollection AddMqttConnectionHandler(this IServiceCollection services)
- {
- services.AddSingleton();
- services.AddSingleton(s => s.GetService());
+ public static IServiceCollection AddMqttConnectionHandler(this IServiceCollection services)
+ {
+ services.AddSingleton();
+ services.AddSingleton(s => s.GetService());
- return services;
- }
+ return services;
+ }
- public static void AddMqttLogger(this IServiceCollection services, IMqttNetLogger logger)
+ public static void AddMqttLogger(this IServiceCollection services, IMqttNetLogger logger)
+ {
+ if (services == null)
{
- if (services == null)
- {
- throw new ArgumentNullException(nameof(services));
- }
-
- services.AddSingleton(logger);
+ throw new ArgumentNullException(nameof(services));
}
- public static IServiceCollection AddMqttServer(this IServiceCollection serviceCollection, Action configure = null)
+ services.AddSingleton(logger);
+ }
+
+ public static IServiceCollection AddMqttServer(this IServiceCollection serviceCollection, Action configure = null)
+ {
+ if (serviceCollection is null)
{
- if (serviceCollection is null)
- {
- throw new ArgumentNullException(nameof(serviceCollection));
- }
+ throw new ArgumentNullException(nameof(serviceCollection));
+ }
- serviceCollection.AddMqttConnectionHandler();
- serviceCollection.AddHostedMqttServer(configure);
+ serviceCollection.AddMqttConnectionHandler();
+ serviceCollection.AddHostedMqttServer(configure);
- return serviceCollection;
- }
+ return serviceCollection;
+ }
- public static IServiceCollection AddMqttTcpServerAdapter(this IServiceCollection services)
- {
- services.AddSingleton();
- services.AddSingleton(s => s.GetService());
+ public static IServiceCollection AddMqttTcpServerAdapter(this IServiceCollection services)
+ {
+ services.AddSingleton();
+ services.AddSingleton(s => s.GetService());
- return services;
- }
+ return services;
+ }
- public static IServiceCollection AddMqttWebSocketServerAdapter(this IServiceCollection services)
- {
- services.AddSingleton();
- services.AddSingleton(s => s.GetService());
+ public static IServiceCollection AddMqttWebSocketServerAdapter(this IServiceCollection services)
+ {
+ services.AddSingleton();
+ services.AddSingleton(s => s.GetService());
- return services;
- }
+ return services;
}
}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/SocketAwaitable.cs b/Source/MQTTnet.AspnetCore/SocketAwaitable.cs
new file mode 100644
index 000000000..2c9607279
--- /dev/null
+++ b/Source/MQTTnet.AspnetCore/SocketAwaitable.cs
@@ -0,0 +1,77 @@
+// 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.Diagnostics;
+using System.IO.Pipelines;
+using System.Net.Sockets;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MQTTnet.AspNetCore;
+
+public class SocketAwaitable : ICriticalNotifyCompletion
+{
+ static readonly Action _callbackCompleted = () =>
+ {
+ };
+
+ readonly PipeScheduler _ioScheduler;
+ int _bytesTransferred;
+
+ Action _callback;
+ SocketError _error;
+
+ public SocketAwaitable(PipeScheduler ioScheduler)
+ {
+ _ioScheduler = ioScheduler;
+ }
+
+ public bool IsCompleted => ReferenceEquals(_callback, _callbackCompleted);
+
+ public void Complete(int bytesTransferred, SocketError socketError)
+ {
+ _error = socketError;
+ _bytesTransferred = bytesTransferred;
+ var continuation = Interlocked.Exchange(ref _callback, _callbackCompleted);
+
+ if (continuation != null)
+ {
+ _ioScheduler.Schedule(state => ((Action)state)(), continuation);
+ }
+ }
+
+ public SocketAwaitable GetAwaiter()
+ {
+ return this;
+ }
+
+ public int GetResult()
+ {
+ Debug.Assert(ReferenceEquals(_callback, _callbackCompleted));
+
+ _callback = null;
+
+ if (_error != SocketError.Success)
+ {
+ throw new SocketException((int)_error);
+ }
+
+ return _bytesTransferred;
+ }
+
+ public void OnCompleted(Action continuation)
+ {
+ if (ReferenceEquals(_callback, _callbackCompleted) || ReferenceEquals(Interlocked.CompareExchange(ref _callback, continuation, null), _callbackCompleted))
+ {
+ Task.Run(continuation);
+ }
+ }
+
+ public void UnsafeOnCompleted(Action continuation)
+ {
+ OnCompleted(continuation);
+ }
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/SocketConnection.cs b/Source/MQTTnet.AspnetCore/SocketConnection.cs
new file mode 100644
index 000000000..2021eccac
--- /dev/null
+++ b/Source/MQTTnet.AspnetCore/SocketConnection.cs
@@ -0,0 +1,261 @@
+// 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 System.IO;
+using System.IO.Pipelines;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Connections;
+using Microsoft.AspNetCore.Http.Features;
+using MQTTnet.Exceptions;
+
+namespace MQTTnet.AspNetCore;
+
+public sealed class SocketConnection : ConnectionContext
+{
+ readonly EndPoint _endPoint;
+ volatile bool _aborted;
+ IDuplexPipe _application;
+ SocketReceiver _receiver;
+ SocketSender _sender;
+
+ Socket _socket;
+
+ public SocketConnection(EndPoint endPoint)
+ {
+ _endPoint = endPoint;
+ }
+
+ public SocketConnection(Socket socket)
+ {
+ _socket = socket;
+ _endPoint = socket.RemoteEndPoint;
+
+ _sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
+ _receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
+ }
+
+ public override string ConnectionId { get; set; }
+ public override IFeatureCollection Features { get; }
+
+ public bool IsConnected { get; private set; }
+ public override IDictionary Items { get; set; }
+ public override IDuplexPipe Transport { get; set; }
+
+ public override ValueTask DisposeAsync()
+ {
+ IsConnected = false;
+
+ Transport?.Output.Complete();
+ Transport?.Input.Complete();
+
+ _socket?.Dispose();
+
+ return base.DisposeAsync();
+ }
+
+ public async Task StartAsync()
+ {
+ if (_socket == null)
+ {
+ _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+ _sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
+ _receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
+ await _socket.ConnectAsync(_endPoint);
+ }
+
+ var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
+
+ Transport = pair.Transport;
+ _application = pair.Application;
+
+ _ = ExecuteAsync();
+
+ IsConnected = true;
+ }
+
+ Exception ConnectionAborted()
+ {
+ return new MqttCommunicationException("Connection Aborted");
+ }
+
+ async Task DoReceive()
+ {
+ Exception error = null;
+
+ try
+ {
+ await ProcessReceives();
+ }
+ catch (SocketException ex) when (ex.SocketErrorCode == SocketError.ConnectionReset)
+ {
+ error = new MqttCommunicationException(ex);
+ }
+ catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted || ex.SocketErrorCode == SocketError.ConnectionAborted ||
+ ex.SocketErrorCode == SocketError.Interrupted || ex.SocketErrorCode == SocketError.InvalidArgument)
+ {
+ if (!_aborted)
+ {
+ // Calling Dispose after ReceiveAsync can cause an "InvalidArgument" error on *nix.
+ error = ConnectionAborted();
+ }
+ }
+ catch (ObjectDisposedException)
+ {
+ if (!_aborted)
+ {
+ error = ConnectionAborted();
+ }
+ }
+ catch (IOException ex)
+ {
+ error = ex;
+ }
+ catch (Exception ex)
+ {
+ error = new IOException(ex.Message, ex);
+ }
+ finally
+ {
+ if (_aborted)
+ {
+ error = error ?? ConnectionAborted();
+ }
+
+ _application.Output.Complete(error);
+ }
+ }
+
+ async Task DoSend()
+ {
+ Exception error = null;
+
+ try
+ {
+ await ProcessSends();
+ }
+ catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted)
+ {
+ }
+ catch (ObjectDisposedException)
+ {
+ }
+ catch (IOException ex)
+ {
+ error = ex;
+ }
+ catch (Exception ex)
+ {
+ error = new IOException(ex.Message, ex);
+ }
+ finally
+ {
+ _aborted = true;
+ _socket.Shutdown(SocketShutdown.Both);
+ }
+
+ return error;
+ }
+
+ async Task ExecuteAsync()
+ {
+ Exception sendError = null;
+ try
+ {
+ // Spawn send and receive logic
+ var receiveTask = DoReceive();
+ var sendTask = DoSend();
+
+ // If the sending task completes then close the receive
+ // We don't need to do this in the other direction because the kestrel
+ // will trigger the output closing once the input is complete.
+ if (await Task.WhenAny(receiveTask, sendTask).ConfigureAwait(false) == sendTask)
+ {
+ // Tell the reader it's being aborted
+ _socket.Dispose();
+ }
+
+ // Now wait for both to complete
+ await receiveTask;
+ sendError = await sendTask;
+
+ // Dispose the socket(should noop if already called)
+ _socket.Dispose();
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Unexpected exception in {nameof(SocketConnection)}.{nameof(StartAsync)}: " + ex);
+ }
+ finally
+ {
+ // Complete the output after disposing the socket
+ await _application.Input.CompleteAsync(sendError).ConfigureAwait(false);
+ }
+ }
+
+ async Task ProcessReceives()
+ {
+ while (true)
+ {
+ // Ensure we have some reasonable amount of buffer space
+ var buffer = _application.Output.GetMemory();
+
+ var bytesReceived = await _receiver.ReceiveAsync(buffer);
+
+ if (bytesReceived == 0)
+ {
+ // FIN
+ break;
+ }
+
+ _application.Output.Advance(bytesReceived);
+
+ var flushTask = _application.Output.FlushAsync();
+
+ if (!flushTask.IsCompleted)
+ {
+ await flushTask;
+ }
+
+ var result = flushTask.GetAwaiter().GetResult();
+ if (result.IsCompleted)
+ {
+ // Pipe consumer is shut down, do we stop writing
+ break;
+ }
+ }
+ }
+
+ async Task ProcessSends()
+ {
+ while (true)
+ {
+ // Wait for data to write from the pipe producer
+ var result = await _application.Input.ReadAsync();
+ var buffer = result.Buffer;
+
+ if (result.IsCanceled)
+ {
+ break;
+ }
+
+ var end = buffer.End;
+ var isCompleted = result.IsCompleted;
+ if (!buffer.IsEmpty)
+ {
+ await _sender.SendAsync(buffer);
+ }
+
+ _application.Input.AdvanceTo(end);
+
+ if (isCompleted)
+ {
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/SocketReceiver.cs b/Source/MQTTnet.AspnetCore/SocketReceiver.cs
new file mode 100644
index 000000000..f8b628fb5
--- /dev/null
+++ b/Source/MQTTnet.AspnetCore/SocketReceiver.cs
@@ -0,0 +1,36 @@
+// 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.IO.Pipelines;
+using System.Net.Sockets;
+
+namespace MQTTnet.AspNetCore;
+
+public sealed class SocketReceiver
+{
+ readonly SocketAwaitable _awaitable;
+ readonly SocketAsyncEventArgs _eventArgs = new();
+ readonly Socket _socket;
+
+ public SocketReceiver(Socket socket, PipeScheduler scheduler)
+ {
+ _socket = socket;
+ _awaitable = new SocketAwaitable(scheduler);
+ _eventArgs.UserToken = _awaitable;
+ _eventArgs.Completed += (_, e) => ((SocketAwaitable)e.UserToken).Complete(e.BytesTransferred, e.SocketError);
+ }
+
+ public SocketAwaitable ReceiveAsync(Memory buffer)
+ {
+ _eventArgs.SetBuffer(buffer);
+
+ if (!_socket.ReceiveAsync(_eventArgs))
+ {
+ _awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
+ }
+
+ return _awaitable;
+ }
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/SocketSender.cs b/Source/MQTTnet.AspnetCore/SocketSender.cs
new file mode 100644
index 000000000..fc06ea6cf
--- /dev/null
+++ b/Source/MQTTnet.AspnetCore/SocketSender.cs
@@ -0,0 +1,93 @@
+// 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.Buffers;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO.Pipelines;
+using System.Net.Sockets;
+using System.Runtime.InteropServices;
+
+namespace MQTTnet.AspNetCore;
+
+public sealed class SocketSender
+{
+ readonly SocketAwaitable _awaitable;
+ readonly SocketAsyncEventArgs _eventArgs = new();
+ readonly Socket _socket;
+
+ List> _bufferList;
+
+ public SocketSender(Socket socket, PipeScheduler scheduler)
+ {
+ _socket = socket;
+ _awaitable = new SocketAwaitable(scheduler);
+ _eventArgs.UserToken = _awaitable;
+ _eventArgs.Completed += (_, e) => ((SocketAwaitable)e.UserToken).Complete(e.BytesTransferred, e.SocketError);
+ }
+
+ public SocketAwaitable SendAsync(in ReadOnlySequence buffers)
+ {
+ if (buffers.IsSingleSegment)
+ {
+ return SendAsync(buffers.First);
+ }
+
+ if (!_eventArgs.MemoryBuffer.Equals(Memory.Empty))
+ {
+ _eventArgs.SetBuffer(null, 0, 0);
+ }
+
+ _eventArgs.BufferList = GetBufferList(buffers);
+
+ if (!_socket.SendAsync(_eventArgs))
+ {
+ _awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
+ }
+
+ return _awaitable;
+ }
+
+ List> GetBufferList(in ReadOnlySequence buffer)
+ {
+ Debug.Assert(!buffer.IsEmpty);
+ Debug.Assert(!buffer.IsSingleSegment);
+
+ if (_bufferList == null)
+ {
+ _bufferList = new List>();
+ }
+ else
+ {
+ // Buffers are pooled, so it's OK to root them until the next multi-buffer write.
+ _bufferList.Clear();
+ }
+
+ foreach (var b in buffer)
+ {
+ _bufferList.Add(b.GetArray());
+ }
+
+ return _bufferList;
+ }
+
+ SocketAwaitable SendAsync(ReadOnlyMemory memory)
+ {
+ // The BufferList getter is much less expensive then the setter.
+ if (_eventArgs.BufferList != null)
+ {
+ _eventArgs.BufferList = null;
+ }
+
+ _eventArgs.SetBuffer(MemoryMarshal.AsMemory(memory));
+
+ if (!_socket.SendAsync(_eventArgs))
+ {
+ _awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
+ }
+
+ return _awaitable;
+ }
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.Benchmarks/ChannelAdapterBenchmark.cs b/Source/MQTTnet.Benchmarks/ChannelAdapterBenchmark.cs
index 3b6a7d858..cbc3f7996 100644
--- a/Source/MQTTnet.Benchmarks/ChannelAdapterBenchmark.cs
+++ b/Source/MQTTnet.Benchmarks/ChannelAdapterBenchmark.cs
@@ -7,7 +7,7 @@
using System.Threading;
using BenchmarkDotNet.Attributes;
using MQTTnet.Adapter;
-using MQTTnet.Diagnostics;
+using MQTTnet.Diagnostics.Logger;
using MQTTnet.Formatter;
using MQTTnet.Packets;
using MQTTnet.Tests.Mockups;
diff --git a/Source/MQTTnet.Benchmarks/LoggerBenchmark.cs b/Source/MQTTnet.Benchmarks/LoggerBenchmark.cs
index 70f0ff51d..6b8b5e410 100644
--- a/Source/MQTTnet.Benchmarks/LoggerBenchmark.cs
+++ b/Source/MQTTnet.Benchmarks/LoggerBenchmark.cs
@@ -4,7 +4,7 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
-using MQTTnet.Diagnostics;
+using MQTTnet.Diagnostics.Logger;
namespace MQTTnet.Benchmarks
{
@@ -15,13 +15,13 @@ public class LoggerBenchmark : BaseBenchmark
{
MqttNetNullLogger _nullLogger;
MqttNetSourceLogger _sourceNullLogger;
-
+
MqttNetEventLogger _eventLogger;
MqttNetSourceLogger _sourceEventLogger;
-
+
MqttNetEventLogger _eventLoggerNoListener;
MqttNetSourceLogger _sourceEventLoggerNoListener;
-
+
bool _useHandler;
[GlobalSetup]
@@ -29,11 +29,11 @@ public void Setup()
{
_nullLogger = new MqttNetNullLogger();
_sourceNullLogger = _nullLogger.WithSource("Source");
-
+
_eventLogger = new MqttNetEventLogger();
_eventLogger.LogMessagePublished += OnLogMessagePublished;
_sourceEventLogger = _eventLogger.WithSource("Source");
-
+
_eventLoggerNoListener = new MqttNetEventLogger();
_sourceEventLoggerNoListener = _eventLoggerNoListener.WithSource("Source");
}
@@ -50,7 +50,7 @@ void OnLogMessagePublished(object sender, MqttNetLogMessagePublishedEventArgs ev
public void Log_10000_Messages_No_Listener()
{
_useHandler = false;
-
+
for (var i = 0; i < 10000; i++)
{
_sourceEventLoggerNoListener.Verbose("test log message {0}", "parameter");
@@ -61,24 +61,24 @@ public void Log_10000_Messages_No_Listener()
public void Log_10000_Messages_With_To_String()
{
_useHandler = true;
-
+
for (var i = 0; i < 10000; i++)
{
_sourceEventLogger.Verbose("test log message {0}", "parameter");
}
}
-
+
[Benchmark]
public void Log_10000_Messages_Without_To_String()
{
_useHandler = false;
-
+
for (var i = 0; i < 10000; i++)
{
_sourceEventLogger.Verbose("test log message {0}", "parameter");
}
}
-
+
[Benchmark]
public void Log_10000_Messages_With_NullLogger()
{
diff --git a/Source/MQTTnet.Benchmarks/MQTTnet.Benchmarks.csproj b/Source/MQTTnet.Benchmarks/MQTTnet.Benchmarks.csproj
index 15c6e2b5e..ceb0ff476 100644
--- a/Source/MQTTnet.Benchmarks/MQTTnet.Benchmarks.csproj
+++ b/Source/MQTTnet.Benchmarks/MQTTnet.Benchmarks.csproj
@@ -4,35 +4,27 @@
true
Exe
Full
- net6.0;net7.0
- 7.2
+ net8.0
false
false
false
true
- 1591;NETSDK1138
+ 1591;NETSDK1138;NU1803;NU1901;NU1902;CS8892
+ false
+ all
+ true
+ latest-Recommended
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
+
+
\ No newline at end of file
diff --git a/Source/MQTTnet.Benchmarks/MessageDeliveryBenchmark.cs b/Source/MQTTnet.Benchmarks/MessageDeliveryBenchmark.cs
index 3738a23a4..e14eb7b1e 100644
--- a/Source/MQTTnet.Benchmarks/MessageDeliveryBenchmark.cs
+++ b/Source/MQTTnet.Benchmarks/MessageDeliveryBenchmark.cs
@@ -2,232 +2,216 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using BenchmarkDotNet.Attributes;
-using MQTTnet.Client;
-using MQTTnet.Server;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using BenchmarkDotNet.Attributes;
+using MQTTnet.Packets;
+using MQTTnet.Server;
-namespace MQTTnet.Benchmarks
-{
- [MemoryDiagnoser]
- public class MessageDeliveryBenchmark : BaseBenchmark
- {
- List _topicPublishMessages;
+namespace MQTTnet.Benchmarks;
- [Params(1, 5)]
- public int NumTopicsPerPublisher;
+[MemoryDiagnoser]
+public class MessageDeliveryBenchmark : BaseBenchmark
+{
+ List _allSubscribedTopics; // Keep track of the subset of topics that are subscribed
+ CancellationTokenSource _cancellationTokenSource;
- [Params(1000, 10000)]
- public int NumPublishers;
+ object _lockMsgCount;
+ int _messagesExpectedCount;
+ int _messagesReceivedCount;
+ Dictionary _mqttPublisherClientsByPublisherName;
- [Params(10)]
- public int NumSubscribers;
+ MqttServer _mqttServer;
+ List _mqttSubscriberClients;
+ Dictionary _publisherByTopic;
+ List _topicPublishMessages;
- [Params(5, 10, 20, 50)]
- public int NumSubscribedTopicsPerSubscriber;
+ Dictionary> _topicsByPublisher;
- object _lockMsgCount;
- int _messagesReceivedCount;
- int _messagesExpectedCount;
- CancellationTokenSource _cancellationTokenSource;
+ [Params(1000, 10000)] public int NumPublishers;
- MqttServer _mqttServer;
- List _mqttSubscriberClients;
- Dictionary _mqttPublisherClientsByPublisherName;
+ [Params(5, 10, 20, 50)] public int NumSubscribedTopicsPerSubscriber;
- Dictionary> _topicsByPublisher;
- Dictionary _publisherByTopic;
- List _allSubscribedTopics; // Keep track of the subset of topics that are subscribed
+ [Params(10)] public int NumSubscribers;
+ [Params(1, 5)] public int NumTopicsPerPublisher;
- [GlobalSetup]
- public void Setup()
+ [GlobalCleanup]
+ public void Cleanup()
+ {
+ foreach (var mp in _mqttPublisherClientsByPublisherName)
{
- _lockMsgCount = new object();
+ var mqttPublisherClient = mp.Value;
+ mqttPublisherClient.DisconnectAsync().GetAwaiter().GetResult();
+ mqttPublisherClient.Dispose();
+ }
- Dictionary> singleWildcardTopicsByPublisher;
- Dictionary> multiWildcardTopicsByPublisher;
+ _mqttPublisherClientsByPublisherName.Clear();
- TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out _topicsByPublisher, out singleWildcardTopicsByPublisher, out multiWildcardTopicsByPublisher);
+ foreach (var mqttSubscriber in _mqttSubscriberClients)
+ {
+ mqttSubscriber.DisconnectAsync().GetAwaiter().GetResult();
+ mqttSubscriber.Dispose();
+ }
- var topics = _topicsByPublisher.First().Value;
- _topicPublishMessages = new List();
- // Prepare messages, same for each publisher
- foreach (var topic in topics)
- {
- var message = new MqttApplicationMessageBuilder()
- .WithTopic(topic)
- .WithPayload(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 })
- .Build();
- _topicPublishMessages.Add(message);
- }
+ _mqttSubscriberClients.Clear();
- // Create server
- var factory = new MqttFactory();
- var serverOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
- _mqttServer = factory.CreateMqttServer(serverOptions);
- _mqttServer.StartAsync().GetAwaiter().GetResult();
+ _mqttServer.StopAsync().GetAwaiter().GetResult();
+ _mqttServer.Dispose();
+ _mqttServer = null;
+ }
- // Create publisher clients
- _mqttPublisherClientsByPublisherName = new Dictionary();
- foreach (var pt in _topicsByPublisher)
- {
- var publisherName = pt.Key;
- var mqttClient = factory.CreateMqttClient();
- var publisherOptions = new MqttClientOptionsBuilder()
- .WithTcpServer("localhost")
- .WithClientId(publisherName)
- .WithKeepAlivePeriod(TimeSpan.FromSeconds(30))
- .Build();
- mqttClient.ConnectAsync(publisherOptions).GetAwaiter().GetResult();
- _mqttPublisherClientsByPublisherName.Add(publisherName, mqttClient);
- }
+ ///
+ /// Publish messages and wait for messages sent to subscribers
+ ///
+ [Benchmark]
+ public void DeliverMessages()
+ {
+ // There should be one message received per publish for each subscribed topic
+ _messagesExpectedCount = NumSubscribedTopicsPerSubscriber * NumSubscribers;
- // Create subscriber clients
- _mqttSubscriberClients = new List();
- for (var i = 0; i < NumSubscribers; i++)
- {
- var mqttSubscriberClient = factory.CreateMqttClient();
- _mqttSubscriberClients.Add(mqttSubscriberClient);
-
- var subscriberOptions = new MqttClientOptionsBuilder()
- .WithTcpServer("localhost")
- .WithClientId("subscriber" + i)
- .Build();
- mqttSubscriberClient.ApplicationMessageReceivedAsync += r =>
- {
- // count messages and signal cancellation when expected message count is reached
- lock (_lockMsgCount)
- {
- ++_messagesReceivedCount;
- if (_messagesReceivedCount == _messagesExpectedCount)
- {
- _cancellationTokenSource.Cancel();
- }
- }
- return Task.CompletedTask;
- };
- mqttSubscriberClient.ConnectAsync(subscriberOptions).GetAwaiter().GetResult();
- }
+ // Loop for a while and exchange messages
+ _messagesReceivedCount = 0;
- List allTopics = new List();
- _publisherByTopic = new Dictionary();
- foreach (var t in _topicsByPublisher)
- {
- foreach (var topic in t.Value)
- {
- _publisherByTopic.Add(topic, t.Key);
- allTopics.Add(topic);
- }
- }
+ _cancellationTokenSource = new CancellationTokenSource();
- // Subscribe to NumSubscribedTopics topics spread across all topics
- _allSubscribedTopics = new List();
+ // same payload for all messages
+ var payload = new byte[] { 1, 2, 3, 4 };
- var totalNumTopics = NumPublishers * NumTopicsPerPublisher;
- int topicIndexStep = totalNumTopics / (NumSubscribedTopicsPerSubscriber * NumSubscribers);
- if (topicIndexStep * NumSubscribedTopicsPerSubscriber * NumSubscribers != totalNumTopics)
- {
- throw new System.Exception(
- String.Format("The total number of topics must be divisible by the number of subscribed topics across all subscribers. Total number of topics: {0}, topic step: {1}",
- totalNumTopics, topicIndexStep
- ));
- }
+ var tasks = new List();
- var topicIndex = 0;
- foreach (var mqttSubscriber in _mqttSubscriberClients)
- {
- for (var i = 0; i < NumSubscribedTopicsPerSubscriber; ++i, topicIndex += topicIndexStep)
- {
- var topic = allTopics[topicIndex];
- _allSubscribedTopics.Add(topic);
- var subOptions = new Client.MqttClientSubscribeOptionsBuilder().WithTopicFilter(
- new Packets.MqttTopicFilter() { Topic = topic })
- .Build();
- mqttSubscriber.SubscribeAsync(subOptions).GetAwaiter().GetResult();
- }
- }
+ // publish a message for each subscribed topic
+ foreach (var topic in _allSubscribedTopics)
+ {
+ var message = new MqttApplicationMessageBuilder().WithTopic(topic).WithPayload(payload).Build();
+ // pick the correct publisher
+ var publisherName = _publisherByTopic[topic];
+ var publisherClient = _mqttPublisherClientsByPublisherName[publisherName];
+ _ = publisherClient.PublishAsync(message);
+ }
- Task.Delay(1000).GetAwaiter().GetResult();
+ // Wait one message per publish to be received by subscriber (in the subscriber's application message handler)
+ try
+ {
+ Task.Delay(30000, _cancellationTokenSource.Token).GetAwaiter().GetResult();
+ }
+ catch
+ {
}
- ///
- /// Publish messages and wait for messages sent to subscribers
- ///
- [Benchmark]
- public void DeliverMessages()
+ _cancellationTokenSource.Dispose();
+
+ if (_messagesReceivedCount < _messagesExpectedCount)
{
- // There should be one message received per publish for each subscribed topic
- _messagesExpectedCount = NumSubscribedTopicsPerSubscriber * NumSubscribers;
+ throw new Exception(string.Format("Messages Received Count mismatch, expected {0}, received {1}", _messagesExpectedCount, _messagesReceivedCount));
+ }
+ }
- // Loop for a while and exchange messages
- _messagesReceivedCount = 0;
+ [GlobalSetup]
+ public void Setup()
+ {
+ _lockMsgCount = new object();
- _cancellationTokenSource = new CancellationTokenSource();
+ Dictionary> singleWildcardTopicsByPublisher;
+ Dictionary> multiWildcardTopicsByPublisher;
- // same payload for all messages
- var payload = new byte[] { 1, 2, 3, 4 };
+ TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out _topicsByPublisher, out singleWildcardTopicsByPublisher, out multiWildcardTopicsByPublisher);
- var tasks = new List();
+ var topics = _topicsByPublisher.First().Value;
+ _topicPublishMessages = new List();
+ // Prepare messages, same for each publisher
+ foreach (var topic in topics)
+ {
+ var message = new MqttApplicationMessageBuilder().WithTopic(topic).WithPayload(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }).Build();
+ _topicPublishMessages.Add(message);
+ }
- // publish a message for each subscribed topic
- foreach (var topic in _allSubscribedTopics)
- {
- var message = new MqttApplicationMessageBuilder()
- .WithTopic(topic)
- .WithPayload(payload)
- .Build();
- // pick the correct publisher
- var publisherName = _publisherByTopic[topic];
- var publisherClient = _mqttPublisherClientsByPublisherName[publisherName];
- _ = publisherClient.PublishAsync(message);
- }
-
- // Wait one message per publish to be received by subscriber (in the subscriber's application message handler)
- try
- {
- Task.Delay(30000, _cancellationTokenSource.Token).GetAwaiter().GetResult();
- }
- catch
- {
+ // Create server
+ var serverFactory = new MqttServerFactory();
+ var clientFactory = new MqttClientFactory();
+ var serverOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
+ _mqttServer = serverFactory.CreateMqttServer(serverOptions);
+ _mqttServer.StartAsync().GetAwaiter().GetResult();
- }
+ // Create publisher clients
+ _mqttPublisherClientsByPublisherName = new Dictionary();
+ foreach (var pt in _topicsByPublisher)
+ {
+ var publisherName = pt.Key;
+ var mqttClient = clientFactory.CreateMqttClient();
+ var publisherOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost").WithClientId(publisherName).WithKeepAlivePeriod(TimeSpan.FromSeconds(30)).Build();
+ mqttClient.ConnectAsync(publisherOptions).GetAwaiter().GetResult();
+ _mqttPublisherClientsByPublisherName.Add(publisherName, mqttClient);
+ }
- _cancellationTokenSource.Dispose();
+ // Create subscriber clients
+ _mqttSubscriberClients = new List();
+ for (var i = 0; i < NumSubscribers; i++)
+ {
+ var mqttSubscriberClient = clientFactory.CreateMqttClient();
+ _mqttSubscriberClients.Add(mqttSubscriberClient);
- if (_messagesReceivedCount < _messagesExpectedCount)
+ var subscriberOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost").WithClientId("subscriber" + i).Build();
+ mqttSubscriberClient.ApplicationMessageReceivedAsync += r =>
{
- throw new Exception(string.Format("Messages Received Count mismatch, expected {0}, received {1}", _messagesExpectedCount, _messagesReceivedCount));
- }
+ // count messages and signal cancellation when expected message count is reached
+ lock (_lockMsgCount)
+ {
+ ++_messagesReceivedCount;
+ if (_messagesReceivedCount == _messagesExpectedCount)
+ {
+ _cancellationTokenSource.Cancel();
+ }
+ }
+
+ return Task.CompletedTask;
+ };
+ mqttSubscriberClient.ConnectAsync(subscriberOptions).GetAwaiter().GetResult();
}
- [GlobalCleanup]
- public void Cleanup()
+
+ var allTopics = new List();
+ _publisherByTopic = new Dictionary();
+ foreach (var t in _topicsByPublisher)
{
- foreach (var mp in _mqttPublisherClientsByPublisherName)
+ foreach (var topic in t.Value)
{
- var mqttPublisherClient = mp.Value;
- mqttPublisherClient.DisconnectAsync().GetAwaiter().GetResult();
- mqttPublisherClient.Dispose();
+ _publisherByTopic.Add(topic, t.Key);
+ allTopics.Add(topic);
}
- _mqttPublisherClientsByPublisherName.Clear();
+ }
+
+ // Subscribe to NumSubscribedTopics topics spread across all topics
+ _allSubscribedTopics = new List();
+
+ var totalNumTopics = NumPublishers * NumTopicsPerPublisher;
+ var topicIndexStep = totalNumTopics / (NumSubscribedTopicsPerSubscriber * NumSubscribers);
+ if (topicIndexStep * NumSubscribedTopicsPerSubscriber * NumSubscribers != totalNumTopics)
+ {
+ throw new Exception(
+ string.Format(
+ "The total number of topics must be divisible by the number of subscribed topics across all subscribers. Total number of topics: {0}, topic step: {1}",
+ totalNumTopics,
+ topicIndexStep));
+ }
- foreach (var mqttSubscriber in _mqttSubscriberClients)
+ var topicIndex = 0;
+ foreach (var mqttSubscriber in _mqttSubscriberClients)
+ {
+ for (var i = 0; i < NumSubscribedTopicsPerSubscriber; ++i, topicIndex += topicIndexStep)
{
- mqttSubscriber.DisconnectAsync().GetAwaiter().GetResult();
- mqttSubscriber.Dispose();
+ var topic = allTopics[topicIndex];
+ _allSubscribedTopics.Add(topic);
+ var subOptions = new MqttClientSubscribeOptionsBuilder().WithTopicFilter(new MqttTopicFilter { Topic = topic }).Build();
+ mqttSubscriber.SubscribeAsync(subOptions).GetAwaiter().GetResult();
}
- _mqttSubscriberClients.Clear();
-
- _mqttServer.StopAsync().GetAwaiter().GetResult();
- _mqttServer.Dispose();
- _mqttServer = null;
}
+
+ Task.Delay(1000).GetAwaiter().GetResult();
}
-}
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.Benchmarks/MessageProcessingBenchmark.cs b/Source/MQTTnet.Benchmarks/MessageProcessingBenchmark.cs
index c5387e767..894ef19e5 100644
--- a/Source/MQTTnet.Benchmarks/MessageProcessingBenchmark.cs
+++ b/Source/MQTTnet.Benchmarks/MessageProcessingBenchmark.cs
@@ -4,48 +4,45 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
-using MQTTnet.Client;
using MQTTnet.Server;
-namespace MQTTnet.Benchmarks
+namespace MQTTnet.Benchmarks;
+
+[SimpleJob(RuntimeMoniker.Net60)]
+[RPlotExporter]
+[RankColumn]
+[MemoryDiagnoser]
+public class MessageProcessingBenchmark : BaseBenchmark
{
- [SimpleJob(RuntimeMoniker.Net60)]
- [RPlotExporter, RankColumn]
- [MemoryDiagnoser]
- public class MessageProcessingBenchmark : BaseBenchmark
- {
- MqttServer _mqttServer;
- IMqttClient _mqttClient;
- MqttApplicationMessage _message;
+ MqttApplicationMessage _message;
+ IMqttClient _mqttClient;
+ MqttServer _mqttServer;
- [GlobalSetup]
- public void Setup()
+ [Benchmark]
+ public void Send_10000_Messages()
+ {
+ for (var i = 0; i < 10000; i++)
{
- var serverOptions = new MqttServerOptionsBuilder().Build();
-
- var factory = new MqttFactory();
- _mqttServer = factory.CreateMqttServer(serverOptions);
- _mqttClient = factory.CreateMqttClient();
+ _mqttClient.PublishAsync(_message).GetAwaiter().GetResult();
+ }
+ }
- _mqttServer.StartAsync().GetAwaiter().GetResult();
+ [GlobalSetup]
+ public void Setup()
+ {
+ var serverOptions = new MqttServerOptionsBuilder().Build();
- var clientOptions = new MqttClientOptionsBuilder()
- .WithTcpServer("localhost").Build();
+ var serverFactory = new MqttServerFactory();
+ _mqttServer = serverFactory.CreateMqttServer(serverOptions);
+ var clientFactory = new MqttClientFactory();
+ _mqttClient = clientFactory.CreateMqttClient();
- _mqttClient.ConnectAsync(clientOptions).GetAwaiter().GetResult();
+ _mqttServer.StartAsync().GetAwaiter().GetResult();
- _message = new MqttApplicationMessageBuilder()
- .WithTopic("A")
- .Build();
- }
+ var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost").Build();
- [Benchmark]
- public void Send_10000_Messages()
- {
- for (var i = 0; i < 10000; i++)
- {
- _mqttClient.PublishAsync(_message).GetAwaiter().GetResult();
- }
- }
+ _mqttClient.ConnectAsync(clientOptions).GetAwaiter().GetResult();
+
+ _message = new MqttApplicationMessageBuilder().WithTopic("A").Build();
}
-}
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.Benchmarks/MessageProcessingMqttConnectionContextBenchmark.cs b/Source/MQTTnet.Benchmarks/MessageProcessingMqttConnectionContextBenchmark.cs
index fd812f461..b22d365e8 100644
--- a/Source/MQTTnet.Benchmarks/MessageProcessingMqttConnectionContextBenchmark.cs
+++ b/Source/MQTTnet.Benchmarks/MessageProcessingMqttConnectionContextBenchmark.cs
@@ -4,12 +4,10 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
-using MQTTnet.Client;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
-using MQTTnet.AspNetCore.Client;
using MQTTnet.AspNetCore;
-using MQTTnet.Diagnostics;
+using MQTTnet.Diagnostics.Logger;
namespace MQTTnet.Benchmarks
{
@@ -38,7 +36,7 @@ public void Setup()
})
.Build();
- var factory = new MqttFactory();
+ var factory = new MqttClientFactory();
_mqttClient = factory.CreateMqttClient(new MqttNetEventLogger(), new MqttClientConnectionContextFactory());
_host.StartAsync().GetAwaiter().GetResult();
diff --git a/Source/MQTTnet.Benchmarks/MqttTcpChannelBenchmark.cs b/Source/MQTTnet.Benchmarks/MqttTcpChannelBenchmark.cs
index 787e951d6..613647471 100644
--- a/Source/MQTTnet.Benchmarks/MqttTcpChannelBenchmark.cs
+++ b/Source/MQTTnet.Benchmarks/MqttTcpChannelBenchmark.cs
@@ -3,89 +3,86 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.Buffers;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Jobs;
using MQTTnet.Channel;
+using MQTTnet.Diagnostics.Logger;
using MQTTnet.Implementations;
using MQTTnet.Server;
-using System.Threading;
-using System.Threading.Tasks;
-using BenchmarkDotNet.Jobs;
-using MQTTnet.Client;
-using MQTTnet.Diagnostics;
+using MQTTnet.Server.Internal.Adapter;
-namespace MQTTnet.Benchmarks
+namespace MQTTnet.Benchmarks;
+
+[SimpleJob(RuntimeMoniker.Net60)]
+[MemoryDiagnoser]
+public class MqttTcpChannelBenchmark : BaseBenchmark
{
- [SimpleJob(RuntimeMoniker.Net60)]
- [MemoryDiagnoser]
- public class MqttTcpChannelBenchmark : BaseBenchmark
+ IMqttChannel _clientChannel;
+ MqttServer _mqttServer;
+ IMqttChannel _serverChannel;
+
+ [Benchmark]
+ public async Task Send_10000_Chunks()
{
- MqttServer _mqttServer;
- IMqttChannel _serverChannel;
- IMqttChannel _clientChannel;
+ var size = 5;
+ var iterations = 10000;
- [GlobalSetup]
- public void Setup()
+ await Task.WhenAll(WriteAsync(iterations, size), ReadAsync(iterations, size));
+ }
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ var serverFactory = new MqttServerFactory();
+ var tcpServer = new MqttTcpServerAdapter();
+ tcpServer.ClientHandler += args =>
{
- var factory = new MqttFactory();
- var tcpServer = new MqttTcpServerAdapter();
- tcpServer.ClientHandler += args =>
- {
- _serverChannel =
- (IMqttChannel)args.GetType().GetField("_channel",
- System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
- .GetValue(args);
+ _serverChannel = (IMqttChannel)args.GetType().GetField("_channel", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(args);
- return Task.CompletedTask;
- };
+ return Task.CompletedTask;
+ };
- var serverOptions = new MqttServerOptionsBuilder().Build();
- _mqttServer = factory.CreateMqttServer(serverOptions, new[] { tcpServer }, new MqttNetEventLogger());
+ var serverOptions = new MqttServerOptionsBuilder().Build();
+ _mqttServer = serverFactory.CreateMqttServer(serverOptions, new[] { tcpServer }, new MqttNetEventLogger());
-
- _mqttServer.StartAsync().GetAwaiter().GetResult();
- var clientOptions = new MqttClientOptionsBuilder()
- .WithTcpServer("localhost").Build();
+ _mqttServer.StartAsync().GetAwaiter().GetResult();
- var tcpOptions = (MqttClientTcpOptions)clientOptions.ChannelOptions;
- _clientChannel = new MqttTcpChannel(new MqttClientOptions { ChannelOptions = tcpOptions });
+ var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost").Build();
- _clientChannel.ConnectAsync(CancellationToken.None).GetAwaiter().GetResult();
- }
+ var tcpOptions = (MqttClientTcpOptions)clientOptions.ChannelOptions;
+ _clientChannel = new MqttTcpChannel(new MqttClientOptions { ChannelOptions = tcpOptions });
- [Benchmark]
- public async Task Send_10000_Chunks()
- {
- var size = 5;
- var iterations = 10000;
+ _clientChannel.ConnectAsync(CancellationToken.None).GetAwaiter().GetResult();
+ }
- await Task.WhenAll(WriteAsync(iterations, size), ReadAsync(iterations, size));
- }
+ async Task ReadAsync(int iterations, int size)
+ {
+ await Task.Yield();
- async Task ReadAsync(int iterations, int size)
+ var expected = iterations * size;
+ long read = 0;
+
+ while (read < expected)
{
- await Task.Yield();
+ var readResult = await _clientChannel.ReadAsync(new byte[size], 0, size, CancellationToken.None).ConfigureAwait(false);
+ read += readResult;
+ }
+ }
- var expected = iterations * size;
- long read = 0;
+ async Task WriteAsync(int iterations, int size)
+ {
+ await Task.Yield();
- while (read < expected)
- {
- var readResult = await _clientChannel.ReadAsync(new byte[size], 0, size, CancellationToken.None).ConfigureAwait(false);
- read += readResult;
- }
- }
+ var buffer = new ReadOnlySequence(new byte[size]);
- async Task WriteAsync(int iterations, int size)
+ for (var i = 0; i < iterations; i++)
{
- await Task.Yield();
-
- var buffer = new ArraySegment(new byte[size]);
-
- for (var i = 0; i < iterations; i++)
- {
- await _serverChannel.WriteAsync(buffer, true, CancellationToken.None).ConfigureAwait(false);
- }
+ await _serverChannel.WriteAsync(buffer, true, CancellationToken.None).ConfigureAwait(false);
}
}
}
\ No newline at end of file
diff --git a/Source/MQTTnet.Benchmarks/Program.cs b/Source/MQTTnet.Benchmarks/Program.cs
index be231ff81..d395269f2 100644
--- a/Source/MQTTnet.Benchmarks/Program.cs
+++ b/Source/MQTTnet.Benchmarks/Program.cs
@@ -71,7 +71,7 @@ static List CollectBenchmarks()
{
if (type != typeof(BaseBenchmark))
{
- benchmarks.Add(type);
+ benchmarks.Add(type);
}
}
}
@@ -87,7 +87,7 @@ static void HandleArguments(string[] arguments)
}
// Allow for preselection to avoid developer frustration.
-
+
if (int.TryParse(arguments[0], out var benchmarkIndex))
{
_selectedBenchmarkIndex = benchmarkIndex;
@@ -111,7 +111,7 @@ static void RenderMenu()
{
Console.Clear();
- Console.WriteLine($"MQTTnet - Benchmarks ({TargetFrameworkProvider.TargetFramework})");
+ Console.WriteLine($"MQTTnet - Benchmarks");
Console.WriteLine("-----------------------------------------------");
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Press arrow keys for benchmark selection");
diff --git a/Source/MQTTnet.Benchmarks/ReaderExtensionsBenchmark.cs b/Source/MQTTnet.Benchmarks/ReaderExtensionsBenchmark.cs
index 4ac0d4f65..cd51297aa 100644
--- a/Source/MQTTnet.Benchmarks/ReaderExtensionsBenchmark.cs
+++ b/Source/MQTTnet.Benchmarks/ReaderExtensionsBenchmark.cs
@@ -31,12 +31,12 @@ public void GlobalSetup()
.WithPayload(new byte[10 * 1024])
.Build();
- var packet = MqttPacketFactories.Publish.Create(mqttMessage);
+ var packet = MqttPublishPacketFactory.Create(mqttMessage);
var buffer = mqttPacketFormatter.Encode(packet);
stream = new MemoryStream();
stream.Write(buffer.Packet);
- stream.Write(buffer.Payload);
+ stream.Write(buffer.Payload.ToArray());
mqttPacketFormatter.Cleanup();
}
diff --git a/Source/MQTTnet.Benchmarks/SendPacketAsyncBenchmark.cs b/Source/MQTTnet.Benchmarks/SendPacketAsyncBenchmark.cs
index aa5cd0383..b31782e66 100644
--- a/Source/MQTTnet.Benchmarks/SendPacketAsyncBenchmark.cs
+++ b/Source/MQTTnet.Benchmarks/SendPacketAsyncBenchmark.cs
@@ -2,6 +2,7 @@
using BenchmarkDotNet.Jobs;
using MQTTnet.Formatter;
using System;
+using System.Buffers;
using System.IO;
using System.IO.Pipelines;
using System.Threading.Tasks;
@@ -40,7 +41,7 @@ public async ValueTask After()
stream.Position = 0;
var output = PipeWriter.Create(stream);
- if (buffer.Payload.Count == 0)
+ if (buffer.Payload.Length == 0)
{
await output.WriteAsync(buffer.Packet).ConfigureAwait(false);
}
@@ -60,7 +61,7 @@ static void WritePacketBuffer(PipeWriter output, MqttPacketBuffer buffer)
var span = output.GetSpan(buffer.Length);
buffer.Packet.AsSpan().CopyTo(span);
- buffer.Payload.AsSpan().CopyTo(span.Slice(buffer.Packet.Count));
+ buffer.Payload.CopyTo(span.Slice(buffer.Packet.Count));
output.Advance(buffer.Length);
}
diff --git a/Source/MQTTnet.Benchmarks/SerializerBenchmark.cs b/Source/MQTTnet.Benchmarks/SerializerBenchmark.cs
index de6ed3dc9..0ddea15f1 100644
--- a/Source/MQTTnet.Benchmarks/SerializerBenchmark.cs
+++ b/Source/MQTTnet.Benchmarks/SerializerBenchmark.cs
@@ -13,7 +13,8 @@
using MQTTnet.Formatter;
using MQTTnet.Formatter.V3;
using BenchmarkDotNet.Jobs;
-using MQTTnet.Diagnostics;
+using MQTTnet.Diagnostics.Logger;
+using System.Buffers;
namespace MQTTnet.Benchmarks
{
@@ -104,7 +105,7 @@ public Task ReadAsync(byte[] buffer, int offset, int count, CancellationTok
return Task.FromResult(count);
}
- public Task WriteAsync(ArraySegment buffer, bool isEndOfPacket, CancellationToken cancellationToken)
+ public Task WriteAsync(ReadOnlySequence buffer, bool isEndOfPacket, CancellationToken cancellationToken)
{
throw new NotSupportedException();
}
diff --git a/Source/MQTTnet.Benchmarks/SubscribeBenchmark.cs b/Source/MQTTnet.Benchmarks/SubscribeBenchmark.cs
index 7825a941e..8c56ee62f 100644
--- a/Source/MQTTnet.Benchmarks/SubscribeBenchmark.cs
+++ b/Source/MQTTnet.Benchmarks/SubscribeBenchmark.cs
@@ -2,65 +2,59 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using BenchmarkDotNet.Attributes;
-using BenchmarkDotNet.Jobs;
-using MQTTnet.Client;
-using MQTTnet.Server;
using System.Collections.Generic;
using System.Linq;
+using BenchmarkDotNet.Attributes;
+using MQTTnet.Server;
-namespace MQTTnet.Benchmarks
+namespace MQTTnet.Benchmarks;
+
+[MemoryDiagnoser]
+public class SubscribeBenchmark : BaseBenchmark
{
- [MemoryDiagnoser]
- public class SubscribeBenchmark : BaseBenchmark
- {
- MqttServer _mqttServer;
- IMqttClient _mqttClient;
+ const int NumPublishers = 1;
+ const int NumTopicsPerPublisher = 10000;
+ IMqttClient _mqttClient;
+ MqttServer _mqttServer;
- const int NumPublishers = 1;
- const int NumTopicsPerPublisher = 10000;
+ List _topics;
- List _topics;
+ [GlobalCleanup]
+ public void Cleanup()
+ {
+ _mqttClient.DisconnectAsync().GetAwaiter().GetResult();
+ _mqttServer.StopAsync().GetAwaiter().GetResult();
+ _mqttServer.Dispose();
+ }
- [GlobalSetup]
- public void Setup()
- {
- TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out var topicsByPublisher, out var singleWildcardTopicsByPublisher, out var multiWildcardTopicsByPublisher);
- _topics = topicsByPublisher.Values.First();
+ [GlobalSetup]
+ public void Setup()
+ {
+ TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out var topicsByPublisher, out var singleWildcardTopicsByPublisher, out var multiWildcardTopicsByPublisher);
+ _topics = topicsByPublisher.Values.First();
- var serverOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
+ var serverOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
- var factory = new MqttFactory();
- _mqttServer = factory.CreateMqttServer(serverOptions);
- _mqttClient = factory.CreateMqttClient();
+ var serverFactory = new MqttServerFactory();
+ _mqttServer = serverFactory.CreateMqttServer(serverOptions);
+ var clientFactory = new MqttClientFactory();
+ _mqttClient = clientFactory.CreateMqttClient();
- _mqttServer.StartAsync().GetAwaiter().GetResult();
+ _mqttServer.StartAsync().GetAwaiter().GetResult();
- var clientOptions = new MqttClientOptionsBuilder()
- .WithTcpServer("localhost").Build();
+ var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost").Build();
- _mqttClient.ConnectAsync(clientOptions).GetAwaiter().GetResult();
- }
+ _mqttClient.ConnectAsync(clientOptions).GetAwaiter().GetResult();
+ }
- [GlobalCleanup]
- public void Cleanup()
+ [Benchmark]
+ public void Subscribe_10000_Topics()
+ {
+ foreach (var topic in _topics)
{
- _mqttClient.DisconnectAsync().GetAwaiter().GetResult();
- _mqttServer.StopAsync().GetAwaiter().GetResult();
- _mqttServer.Dispose();
- }
+ var subscribeOptions = new MqttClientSubscribeOptionsBuilder().WithTopicFilter(topic).Build();
- [Benchmark]
- public void Subscribe_10000_Topics()
- {
- foreach (var topic in _topics)
- {
- var subscribeOptions = new MqttClientSubscribeOptionsBuilder()
- .WithTopicFilter(topic, Protocol.MqttQualityOfServiceLevel.AtMostOnce)
- .Build();
-
- _mqttClient.SubscribeAsync(subscribeOptions).GetAwaiter().GetResult();
- }
+ _mqttClient.SubscribeAsync(subscribeOptions).GetAwaiter().GetResult();
}
}
-}
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.Benchmarks/TcpPipesBenchmark.cs b/Source/MQTTnet.Benchmarks/TcpPipesBenchmark.cs
index 32e45a3ac..7692f78b3 100644
--- a/Source/MQTTnet.Benchmarks/TcpPipesBenchmark.cs
+++ b/Source/MQTTnet.Benchmarks/TcpPipesBenchmark.cs
@@ -9,7 +9,7 @@
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
-using MQTTnet.AspNetCore.Client.Tcp;
+using MQTTnet.AspNetCore;
namespace MQTTnet.Benchmarks
{
diff --git a/Source/MQTTnet.Benchmarks/UnsubscribeBenchmark.cs b/Source/MQTTnet.Benchmarks/UnsubscribeBenchmark.cs
index e18a71dff..41f3adcdb 100644
--- a/Source/MQTTnet.Benchmarks/UnsubscribeBenchmark.cs
+++ b/Source/MQTTnet.Benchmarks/UnsubscribeBenchmark.cs
@@ -2,63 +2,56 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using BenchmarkDotNet.Attributes;
-using MQTTnet.Client;
-using MQTTnet.Server;
using System.Collections.Generic;
using System.Linq;
+using BenchmarkDotNet.Attributes;
+using MQTTnet.Server;
-namespace MQTTnet.Benchmarks
-{
- [MemoryDiagnoser]
- public class UnsubscribeBenchmark : BaseBenchmark
- {
- MqttServer _mqttServer;
- IMqttClient _mqttClient;
+namespace MQTTnet.Benchmarks;
- const int NumPublishers = 1;
- const int NumTopicsPerPublisher = 10000;
+[MemoryDiagnoser]
+public class UnsubscribeBenchmark : BaseBenchmark
+{
+ const int NumPublishers = 1;
+ const int NumTopicsPerPublisher = 10000;
+ IMqttClient _mqttClient;
+ MqttServer _mqttServer;
- List _topics;
+ List _topics;
- [GlobalSetup]
- public void Setup()
- {
- TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out var topicsByPublisher, out var singleWildcardTopicsByPublisher, out var multiWildcardTopicsByPublisher);
- _topics = topicsByPublisher.Values.First();
+ [GlobalSetup]
+ public void Setup()
+ {
+ TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out var topicsByPublisher, out var singleWildcardTopicsByPublisher, out var multiWildcardTopicsByPublisher);
+ _topics = topicsByPublisher.Values.First();
- var serverOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
+ var serverOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
- var factory = new MqttFactory();
- _mqttServer = factory.CreateMqttServer(serverOptions);
- _mqttClient = factory.CreateMqttClient();
+ var serverFactory = new MqttServerFactory();
+ _mqttServer = serverFactory.CreateMqttServer(serverOptions);
+ var clientFactory = new MqttClientFactory();
+ _mqttClient = clientFactory.CreateMqttClient();
- _mqttServer.StartAsync().GetAwaiter().GetResult();
+ _mqttServer.StartAsync().GetAwaiter().GetResult();
- var clientOptions = new MqttClientOptionsBuilder()
- .WithTcpServer("localhost").Build();
+ var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost").Build();
- _mqttClient.ConnectAsync(clientOptions).GetAwaiter().GetResult();
+ _mqttClient.ConnectAsync(clientOptions).GetAwaiter().GetResult();
- foreach (var topic in _topics)
- {
- var subscribeOptions = new MqttClientSubscribeOptionsBuilder()
- .WithTopicFilter(topic)
- .Build();
- _mqttClient.SubscribeAsync(subscribeOptions).GetAwaiter().GetResult();
- }
+ foreach (var topic in _topics)
+ {
+ var subscribeOptions = new MqttClientSubscribeOptionsBuilder().WithTopicFilter(topic).Build();
+ _mqttClient.SubscribeAsync(subscribeOptions).GetAwaiter().GetResult();
}
+ }
- [Benchmark]
- public void Unsubscribe_10000_Topics()
+ [Benchmark]
+ public void Unsubscribe_10000_Topics()
+ {
+ foreach (var topic in _topics)
{
- foreach (var topic in _topics)
- {
- var unsubscribeOptions = new MqttClientUnsubscribeOptionsBuilder()
- .WithTopicFilter(topic)
- .Build();
- _mqttClient.UnsubscribeAsync(unsubscribeOptions).GetAwaiter().GetResult();
- }
+ var unsubscribeOptions = new MqttClientUnsubscribeOptionsBuilder().WithTopicFilter(topic).Build();
+ _mqttClient.UnsubscribeAsync(unsubscribeOptions).GetAwaiter().GetResult();
}
}
-}
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageProcessedEventArgs.cs b/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageProcessedEventArgs.cs
deleted file mode 100644
index 2efaa4933..000000000
--- a/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageProcessedEventArgs.cs
+++ /dev/null
@@ -1,24 +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;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public sealed class ApplicationMessageProcessedEventArgs : EventArgs
- {
- public ApplicationMessageProcessedEventArgs(ManagedMqttApplicationMessage applicationMessage, Exception exception)
- {
- ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
- Exception = exception;
- }
-
- public ManagedMqttApplicationMessage ApplicationMessage { get; }
-
- ///
- /// Then this is _null_ the message was processed successfully without any error.
- ///
- public Exception Exception { get; }
- }
-}
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageSkippedEventArgs.cs b/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageSkippedEventArgs.cs
deleted file mode 100644
index 0afd96a48..000000000
--- a/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageSkippedEventArgs.cs
+++ /dev/null
@@ -1,18 +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;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public sealed class ApplicationMessageSkippedEventArgs : EventArgs
- {
- public ApplicationMessageSkippedEventArgs(ManagedMqttApplicationMessage applicationMessage)
- {
- ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
- }
-
- public ManagedMqttApplicationMessage ApplicationMessage { get; }
- }
-}
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ConnectingFailedEventArgs.cs b/Source/MQTTnet.Extensions.ManagedClient/ConnectingFailedEventArgs.cs
deleted file mode 100644
index 5fd7bdd7b..000000000
--- a/Source/MQTTnet.Extensions.ManagedClient/ConnectingFailedEventArgs.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-using MQTTnet.Client;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public sealed class ConnectingFailedEventArgs : EventArgs
- {
- public ConnectingFailedEventArgs(MqttClientConnectResult connectResult, Exception exception)
- {
- ConnectResult = connectResult;
- Exception = exception;
- }
-
- ///
- /// This is null when the connection was failing and the server was not reachable.
- ///
- public MqttClientConnectResult ConnectResult { get; }
-
- public Exception Exception { get; }
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs b/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs
deleted file mode 100644
index 968fe6997..000000000
--- a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MQTTnet.Client;
-using MQTTnet.Packets;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public interface IManagedMqttClient : IDisposable
- {
- event Func ApplicationMessageProcessedAsync;
-
- event Func ApplicationMessageReceivedAsync;
-
- event Func ApplicationMessageSkippedAsync;
-
- event Func ConnectedAsync;
-
- event Func ConnectingFailedAsync;
-
- event Func ConnectionStateChangedAsync;
-
- event Func DisconnectedAsync;
-
- event Func SynchronizingSubscriptionsFailedAsync;
-
- event Func SubscriptionsChangedAsync;
-
- IMqttClient InternalClient { get; }
-
- bool IsConnected { get; }
-
- bool IsStarted { get; }
-
- ManagedMqttClientOptions Options { get; }
-
- int PendingApplicationMessagesCount { get; }
-
- Task EnqueueAsync(MqttApplicationMessage applicationMessage);
-
- Task EnqueueAsync(ManagedMqttApplicationMessage applicationMessage);
-
- Task PingAsync(CancellationToken cancellationToken = default);
-
- Task StartAsync(ManagedMqttClientOptions options);
-
- Task StopAsync(bool cleanDisconnect = true);
-
- Task SubscribeAsync(IEnumerable topicFilters);
-
- Task UnsubscribeAsync(IEnumerable topics);
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClientStorage.cs b/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClientStorage.cs
deleted file mode 100644
index 09bd4105d..000000000
--- a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClientStorage.cs
+++ /dev/null
@@ -1,16 +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.Collections.Generic;
-using System.Threading.Tasks;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public interface IManagedMqttClientStorage
- {
- Task SaveQueuedMessagesAsync(IList messages);
-
- Task> LoadQueuedMessagesAsync();
- }
-}
diff --git a/Source/MQTTnet.Extensions.ManagedClient/InterceptingPublishMessageEventArgs.cs b/Source/MQTTnet.Extensions.ManagedClient/InterceptingPublishMessageEventArgs.cs
deleted file mode 100644
index c688082fa..000000000
--- a/Source/MQTTnet.Extensions.ManagedClient/InterceptingPublishMessageEventArgs.cs
+++ /dev/null
@@ -1,20 +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;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public sealed class InterceptingPublishMessageEventArgs : EventArgs
- {
- public InterceptingPublishMessageEventArgs(ManagedMqttApplicationMessage applicationMessage)
- {
- ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
- }
-
- public ManagedMqttApplicationMessage ApplicationMessage { get; }
-
- public bool AcceptPublish { get; set; } = true;
- }
-}
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttApplicationMessage.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttApplicationMessage.cs
deleted file mode 100644
index 7ef38ebd2..000000000
--- a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttApplicationMessage.cs
+++ /dev/null
@@ -1,15 +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;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public class ManagedMqttApplicationMessage
- {
- public Guid Id { get; set; } = Guid.NewGuid();
-
- public MqttApplicationMessage ApplicationMessage { get; set; }
- }
-}
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttApplicationMessageBuilder.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttApplicationMessageBuilder.cs
deleted file mode 100644
index 96d0a42b3..000000000
--- a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttApplicationMessageBuilder.cs
+++ /dev/null
@@ -1,51 +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;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public class ManagedMqttApplicationMessageBuilder
- {
- private Guid _id = Guid.NewGuid();
- private MqttApplicationMessage _applicationMessage;
-
- public ManagedMqttApplicationMessageBuilder WithId(Guid id)
- {
- _id = id;
- return this;
- }
-
- public ManagedMqttApplicationMessageBuilder WithApplicationMessage(MqttApplicationMessage applicationMessage)
- {
- _applicationMessage = applicationMessage;
- return this;
- }
-
- public ManagedMqttApplicationMessageBuilder WithApplicationMessage(Action builder)
- {
- if (builder == null) throw new ArgumentNullException(nameof(builder));
-
- var internalBuilder = new MqttApplicationMessageBuilder();
- builder(internalBuilder);
-
- _applicationMessage = internalBuilder.Build();
- return this;
- }
-
- public ManagedMqttApplicationMessage Build()
- {
- if (_applicationMessage == null)
- {
- throw new InvalidOperationException("The ApplicationMessage cannot be null.");
- }
-
- return new ManagedMqttApplicationMessage
- {
- Id = _id,
- ApplicationMessage = _applicationMessage
- };
- }
- }
-}
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClient.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClient.cs
deleted file mode 100644
index 679f69293..000000000
--- a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClient.cs
+++ /dev/null
@@ -1,827 +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 System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MQTTnet.Client;
-using MQTTnet.Diagnostics;
-using MQTTnet.Exceptions;
-using MQTTnet.Internal;
-using MQTTnet.Packets;
-using MQTTnet.Protocol;
-using MQTTnet.Server;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public sealed class ManagedMqttClient : Disposable, IManagedMqttClient
- {
- readonly MqttNetSourceLogger _logger;
-
- readonly AsyncEvent _interceptingPublishMessageEvent = new AsyncEvent();
- readonly AsyncEvent _applicationMessageProcessedEvent = new AsyncEvent();
- readonly AsyncEvent _applicationMessageSkippedEvent = new AsyncEvent();
- readonly AsyncEvent _connectingFailedEvent = new AsyncEvent();
- readonly AsyncEvent _connectionStateChangedEvent = new AsyncEvent();
- readonly AsyncEvent _synchronizingSubscriptionsFailedEvent = new AsyncEvent();
- readonly AsyncEvent _subscriptionsChangedEvent = new AsyncEvent();
-
- readonly BlockingQueue _messageQueue = new BlockingQueue();
- readonly AsyncLock _messageQueueLock = new AsyncLock();
-
- ///
- /// The subscriptions are managed in 2 separate buckets:
- ///
- /// and
- ///
- /// are processed during normal operation
- /// and are moved to the
- ///
- /// when they get processed. They can be accessed by
- /// any thread and are therefore mutex'ed.
- ///
- /// get sent to the broker
- /// at reconnect and are solely owned by
- ///
- /// .
- ///
- readonly Dictionary _reconnectSubscriptions = new Dictionary();
-
- readonly Dictionary _subscriptions = new Dictionary();
- readonly SemaphoreSlim _subscriptionsQueuedSignal = new SemaphoreSlim(0);
- readonly HashSet _unsubscriptions = new HashSet();
-
- CancellationTokenSource _connectionCancellationToken;
- Task _maintainConnectionTask;
- CancellationTokenSource _publishingCancellationToken;
-
- ManagedMqttClientStorageManager _storageManager;
- bool _isCleanDisconnect;
-
- public ManagedMqttClient(IMqttClient mqttClient, IMqttNetLogger logger)
- {
- InternalClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
-
- if (logger == null)
- {
- throw new ArgumentNullException(nameof(logger));
- }
-
- _logger = logger.WithSource(nameof(ManagedMqttClient));
- }
-
- public event Func ApplicationMessageSkippedAsync
- {
- add => _applicationMessageSkippedEvent.AddHandler(value);
- remove => _applicationMessageSkippedEvent.RemoveHandler(value);
- }
-
- public event Func ApplicationMessageProcessedAsync
- {
- add => _applicationMessageProcessedEvent.AddHandler(value);
- remove => _applicationMessageProcessedEvent.RemoveHandler(value);
- }
-
- public event Func InterceptPublishMessageAsync
- {
- add => _interceptingPublishMessageEvent.AddHandler(value);
- remove => _interceptingPublishMessageEvent.RemoveHandler(value);
- }
-
- public event Func ApplicationMessageReceivedAsync
- {
- add => InternalClient.ApplicationMessageReceivedAsync += value;
- remove => InternalClient.ApplicationMessageReceivedAsync -= value;
- }
-
- public event Func ConnectedAsync
- {
- add => InternalClient.ConnectedAsync += value;
- remove => InternalClient.ConnectedAsync -= value;
- }
-
- public event Func ConnectingFailedAsync
- {
- add => _connectingFailedEvent.AddHandler(value);
- remove => _connectingFailedEvent.RemoveHandler(value);
- }
-
- public event Func ConnectionStateChangedAsync
- {
- add => _connectionStateChangedEvent.AddHandler(value);
- remove => _connectionStateChangedEvent.RemoveHandler(value);
- }
-
- public event Func DisconnectedAsync
- {
- add => InternalClient.DisconnectedAsync += value;
- remove => InternalClient.DisconnectedAsync -= value;
- }
-
- public event Func SynchronizingSubscriptionsFailedAsync
- {
- add => _synchronizingSubscriptionsFailedEvent.AddHandler(value);
- remove => _synchronizingSubscriptionsFailedEvent.RemoveHandler(value);
- }
-
- public event Func SubscriptionsChangedAsync
- {
- add => _subscriptionsChangedEvent.AddHandler(value);
- remove => _subscriptionsChangedEvent.RemoveHandler(value);
- }
-
- public IMqttClient InternalClient { get; }
-
- public bool IsConnected => InternalClient.IsConnected;
-
- public bool IsStarted => _connectionCancellationToken != null;
-
- public ManagedMqttClientOptions Options { get; private set; }
-
- public int PendingApplicationMessagesCount => _messageQueue.Count;
-
- public async Task EnqueueAsync(MqttApplicationMessage applicationMessage)
- {
- ThrowIfDisposed();
-
- if (applicationMessage == null)
- {
- throw new ArgumentNullException(nameof(applicationMessage));
- }
-
- var managedMqttApplicationMessage = new ManagedMqttApplicationMessageBuilder().WithApplicationMessage(applicationMessage);
- await EnqueueAsync(managedMqttApplicationMessage.Build()).ConfigureAwait(false);
- }
-
- public async Task EnqueueAsync(ManagedMqttApplicationMessage applicationMessage)
- {
- ThrowIfDisposed();
-
- if (applicationMessage == null)
- {
- throw new ArgumentNullException(nameof(applicationMessage));
- }
-
- if (Options == null)
- {
- throw new InvalidOperationException("call StartAsync before publishing messages");
- }
-
- MqttTopicValidator.ThrowIfInvalid(applicationMessage.ApplicationMessage);
-
- ManagedMqttApplicationMessage removedMessage = null;
- ApplicationMessageSkippedEventArgs applicationMessageSkippedEventArgs = null;
-
- try
- {
- using (await _messageQueueLock.EnterAsync().ConfigureAwait(false))
- {
- if (_messageQueue.Count >= Options.MaxPendingMessages)
- {
- if (Options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropNewMessage)
- {
- _logger.Verbose("Skipping publish of new application message because internal queue is full.");
- applicationMessageSkippedEventArgs = new ApplicationMessageSkippedEventArgs(applicationMessage);
- return;
- }
-
- if (Options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage)
- {
- removedMessage = _messageQueue.RemoveFirst();
- _logger.Verbose("Removed oldest application message from internal queue because it is full.");
- applicationMessageSkippedEventArgs = new ApplicationMessageSkippedEventArgs(removedMessage);
- }
- }
-
- _messageQueue.Enqueue(applicationMessage);
-
- if (_storageManager != null)
- {
- if (removedMessage != null)
- {
- await _storageManager.RemoveAsync(removedMessage).ConfigureAwait(false);
- }
-
- await _storageManager.AddAsync(applicationMessage).ConfigureAwait(false);
- }
- }
- }
- finally
- {
- if (applicationMessageSkippedEventArgs != null && _applicationMessageSkippedEvent.HasHandlers)
- {
- await _applicationMessageSkippedEvent.InvokeAsync(applicationMessageSkippedEventArgs).ConfigureAwait(false);
- }
- }
- }
-
- public Task PingAsync(CancellationToken cancellationToken = default)
- {
- return InternalClient.PingAsync(cancellationToken);
- }
-
- public async Task StartAsync(ManagedMqttClientOptions options)
- {
- ThrowIfDisposed();
-
- if (options == null)
- {
- throw new ArgumentNullException(nameof(options));
- }
-
- if (options.ClientOptions == null)
- {
- throw new ArgumentException("The client options are not set.", nameof(options));
- }
-
- if (!_maintainConnectionTask?.IsCompleted ?? false)
- {
- throw new InvalidOperationException("The managed client is already started.");
- }
-
- Options = options;
-
- if (options.Storage != null)
- {
- _storageManager = new ManagedMqttClientStorageManager(options.Storage);
- var messages = await _storageManager.LoadQueuedMessagesAsync().ConfigureAwait(false);
-
- foreach (var message in messages)
- {
- _messageQueue.Enqueue(message);
- }
- }
-
- var cancellationTokenSource = new CancellationTokenSource();
- var cancellationToken = cancellationTokenSource.Token;
- _connectionCancellationToken = cancellationTokenSource;
-
- _maintainConnectionTask = Task.Run(() => MaintainConnectionAsync(cancellationToken), cancellationToken);
- _maintainConnectionTask.RunInBackground(_logger);
-
- _logger.Info("Started");
- }
-
- public async Task StopAsync(bool cleanDisconnect = true)
- {
- ThrowIfDisposed();
-
- _isCleanDisconnect = cleanDisconnect;
-
- StopPublishing();
- StopMaintainingConnection();
-
- _messageQueue.Clear();
-
- if (_maintainConnectionTask != null)
- {
- await Task.WhenAny(_maintainConnectionTask);
- _maintainConnectionTask = null;
- }
- }
-
- public Task SubscribeAsync(IEnumerable topicFilters)
- {
- ThrowIfDisposed();
-
- if (topicFilters == null)
- {
- throw new ArgumentNullException(nameof(topicFilters));
- }
-
- foreach (var topicFilter in topicFilters)
- {
- MqttTopicValidator.ThrowIfInvalidSubscribe(topicFilter.Topic);
- }
-
- lock (_subscriptions)
- {
- foreach (var topicFilter in topicFilters)
- {
- _subscriptions[topicFilter.Topic] = topicFilter;
- _unsubscriptions.Remove(topicFilter.Topic);
- }
- }
-
- _subscriptionsQueuedSignal.Release();
-
- return CompletedTask.Instance;
- }
-
- public Task UnsubscribeAsync(IEnumerable topics)
- {
- ThrowIfDisposed();
-
- if (topics == null)
- {
- throw new ArgumentNullException(nameof(topics));
- }
-
- lock (_subscriptions)
- {
- foreach (var topic in topics)
- {
- _subscriptions.Remove(topic);
- _unsubscriptions.Add(topic);
- }
- }
-
- _subscriptionsQueuedSignal.Release();
-
- return CompletedTask.Instance;
- }
-
- protected override void Dispose(bool disposing)
- {
- if (disposing)
- {
- StopPublishing();
- StopMaintainingConnection();
-
- if (_maintainConnectionTask != null)
- {
- _maintainConnectionTask.GetAwaiter().GetResult();
- _maintainConnectionTask = null;
- }
-
- _messageQueue.Dispose();
- _messageQueueLock.Dispose();
- InternalClient.Dispose();
- _subscriptionsQueuedSignal.Dispose();
- }
-
- base.Dispose(disposing);
- }
-
- static TimeSpan GetRemainingTime(DateTime endTime)
- {
- var remainingTime = endTime - DateTime.UtcNow;
- return remainingTime < TimeSpan.Zero ? TimeSpan.Zero : remainingTime;
- }
-
- CancellationTokenSource NewTimeoutToken(CancellationToken linkedToken)
- {
- var newTimeoutToken = CancellationTokenSource.CreateLinkedTokenSource(linkedToken);
- newTimeoutToken.CancelAfter(Options.ClientOptions.Timeout);
- return newTimeoutToken;
- }
-
- async Task HandleSubscriptionExceptionAsync(Exception exception, List addedSubscriptions, List removedSubscriptions)
- {
- _logger.Warning(exception, "Synchronizing subscriptions failed.");
-
- if (_synchronizingSubscriptionsFailedEvent.HasHandlers)
- {
- await _synchronizingSubscriptionsFailedEvent.InvokeAsync(new ManagedProcessFailedEventArgs(exception, addedSubscriptions, removedSubscriptions)).ConfigureAwait(false);
- }
- }
-
- async Task HandleSubscriptionsResultAsync(SendSubscribeUnsubscribeResult subscribeUnsubscribeResult)
- {
- if (_subscriptionsChangedEvent.HasHandlers)
- {
- await _subscriptionsChangedEvent.InvokeAsync(new SubscriptionsChangedEventArgs(subscribeUnsubscribeResult.SubscribeResults, subscribeUnsubscribeResult.UnsubscribeResults)).ConfigureAwait(false);
- }
- }
-
- async Task MaintainConnectionAsync(CancellationToken cancellationToken)
- {
- try
- {
- while (!cancellationToken.IsCancellationRequested)
- {
- await TryMaintainConnectionAsync(cancellationToken).ConfigureAwait(false);
- }
- }
- catch (OperationCanceledException)
- {
- }
- catch (Exception exception)
- {
- _logger.Error(exception, "Error exception while maintaining connection.");
- }
- finally
- {
- if (!IsDisposed)
- {
- try
- {
- if (_isCleanDisconnect)
- {
- using (var disconnectTimeout = NewTimeoutToken(CancellationToken.None))
- {
- await InternalClient.DisconnectAsync(new MqttClientDisconnectOptions(), disconnectTimeout.Token).ConfigureAwait(false);
- }
- }
- }
- catch (OperationCanceledException)
- {
- _logger.Warning("Timeout while sending DISCONNECT packet.");
- }
- catch (Exception exception)
- {
- _logger.Error(exception, "Error while disconnecting.");
- }
-
- _logger.Info("Stopped");
- }
-
- _reconnectSubscriptions.Clear();
-
- lock (_subscriptions)
- {
- _subscriptions.Clear();
- _unsubscriptions.Clear();
- }
- }
- }
-
- async Task PublishQueuedMessagesAsync(CancellationToken cancellationToken)
- {
- try
- {
- while (!cancellationToken.IsCancellationRequested && InternalClient.IsConnected)
- {
- // Peek at the message without dequeueing in order to prevent the
- // possibility of the queue growing beyond the configured cap.
- // Previously, messages could be re-enqueued if there was an
- // exception, and this re-enqueueing did not honor the cap.
- // Furthermore, because re-enqueueing would shuffle the order
- // of the messages, the DropOldestQueuedMessage strategy would
- // be unable to know which message is actually the oldest and would
- // instead drop the first item in the queue.
- var message = _messageQueue.PeekAndWait(cancellationToken);
- if (message == null)
- {
- continue;
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- await TryPublishQueuedMessageAsync(message, cancellationToken).ConfigureAwait(false);
- }
- }
- catch (OperationCanceledException)
- {
- }
- catch (Exception exception)
- {
- _logger.Error(exception, "Error while publishing queued application messages.");
- }
- finally
- {
- _logger.Verbose("Stopped publishing messages.");
- }
- }
-
- async Task PublishReconnectSubscriptionsAsync(CancellationToken cancellationToken)
- {
- _logger.Info("Publishing subscriptions at reconnect");
-
- List topicFilters = null;
-
- try
- {
- if (_reconnectSubscriptions.Any())
- {
- topicFilters = new List();
- SendSubscribeUnsubscribeResult subscribeUnsubscribeResult;
-
- foreach (var sub in _reconnectSubscriptions)
- {
- topicFilters.Add(sub.Value);
-
- if (topicFilters.Count == Options.MaxTopicFiltersInSubscribeUnsubscribePackets)
- {
- subscribeUnsubscribeResult = await SendSubscribeUnsubscribe(topicFilters, null, cancellationToken).ConfigureAwait(false);
- topicFilters.Clear();
- await HandleSubscriptionsResultAsync(subscribeUnsubscribeResult).ConfigureAwait(false);
- }
- }
-
- subscribeUnsubscribeResult = await SendSubscribeUnsubscribe(topicFilters, null, cancellationToken).ConfigureAwait(false);
- await HandleSubscriptionsResultAsync(subscribeUnsubscribeResult).ConfigureAwait(false);
- }
- }
- catch (Exception exception)
- {
- await HandleSubscriptionExceptionAsync(exception, topicFilters, null).ConfigureAwait(false);
- }
- }
-
- async Task PublishSubscriptionsAsync(TimeSpan timeout, CancellationToken cancellationToken)
- {
- var endTime = DateTime.UtcNow + timeout;
-
- while (await _subscriptionsQueuedSignal.WaitAsync(GetRemainingTime(endTime), cancellationToken).ConfigureAwait(false))
- {
- List subscriptions;
- SendSubscribeUnsubscribeResult subscribeUnsubscribeResult;
- HashSet unsubscriptions;
-
- lock (_subscriptions)
- {
- subscriptions = _subscriptions.Values.ToList();
- _subscriptions.Clear();
-
- unsubscriptions = new HashSet(_unsubscriptions);
- _unsubscriptions.Clear();
- }
-
- if (!subscriptions.Any() && !unsubscriptions.Any())
- {
- continue;
- }
-
- _logger.Verbose("Publishing {0} added and {1} removed subscriptions", subscriptions.Count, unsubscriptions.Count);
-
- foreach (var unsubscription in unsubscriptions)
- {
- _reconnectSubscriptions.Remove(unsubscription);
- }
-
- foreach (var subscription in subscriptions)
- {
- _reconnectSubscriptions[subscription.Topic] = subscription;
- }
-
- var addedTopicFilters = new List();
- foreach (var subscription in subscriptions)
- {
- addedTopicFilters.Add(subscription);
-
- if (addedTopicFilters.Count == Options.MaxTopicFiltersInSubscribeUnsubscribePackets)
- {
- subscribeUnsubscribeResult = await SendSubscribeUnsubscribe(addedTopicFilters, null, cancellationToken).ConfigureAwait(false);
- addedTopicFilters.Clear();
- await HandleSubscriptionsResultAsync(subscribeUnsubscribeResult).ConfigureAwait(false);
- }
- }
-
- subscribeUnsubscribeResult = await SendSubscribeUnsubscribe(addedTopicFilters, null, cancellationToken).ConfigureAwait(false);
- await HandleSubscriptionsResultAsync(subscribeUnsubscribeResult).ConfigureAwait(false);
-
- var removedTopicFilters = new List();
- foreach (var unSub in unsubscriptions)
- {
- removedTopicFilters.Add(unSub);
-
- if (removedTopicFilters.Count == Options.MaxTopicFiltersInSubscribeUnsubscribePackets)
- {
- subscribeUnsubscribeResult = await SendSubscribeUnsubscribe(null, removedTopicFilters, cancellationToken).ConfigureAwait(false);
- removedTopicFilters.Clear();
- await HandleSubscriptionsResultAsync(subscribeUnsubscribeResult).ConfigureAwait(false);
- }
- }
-
- subscribeUnsubscribeResult = await SendSubscribeUnsubscribe(null, removedTopicFilters, cancellationToken).ConfigureAwait(false);
- await HandleSubscriptionsResultAsync(subscribeUnsubscribeResult).ConfigureAwait(false);
- }
- }
-
- async Task ReconnectIfRequiredAsync(CancellationToken cancellationToken)
- {
- if (InternalClient.IsConnected)
- {
- return ReconnectionResult.StillConnected;
- }
-
- MqttClientConnectResult connectResult = null;
- try
- {
- using (var connectTimeout = NewTimeoutToken(cancellationToken))
- {
- connectResult = await InternalClient.ConnectAsync(Options.ClientOptions, connectTimeout.Token).ConfigureAwait(false);
- }
-
- if (connectResult.ResultCode != MqttClientConnectResultCode.Success)
- {
- throw new MqttCommunicationException($"Client connected but server denied connection with reason '{connectResult.ResultCode}'.");
- }
-
- return connectResult.IsSessionPresent ? ReconnectionResult.Recovered : ReconnectionResult.Reconnected;
- }
- catch (Exception exception)
- {
- await _connectingFailedEvent.InvokeAsync(new ConnectingFailedEventArgs(connectResult, exception));
- return ReconnectionResult.NotConnected;
- }
- }
-
- async Task SendSubscribeUnsubscribe(List addedSubscriptions, List removedSubscriptions, CancellationToken cancellationToken)
- {
- var subscribeResults = new List();
- var unsubscribeResults = new List();
- try
- {
- if (removedSubscriptions != null && removedSubscriptions.Any())
- {
- var unsubscribeOptionsBuilder = new MqttClientUnsubscribeOptionsBuilder();
-
- foreach (var removedSubscription in removedSubscriptions)
- {
- unsubscribeOptionsBuilder.WithTopicFilter(removedSubscription);
- }
-
- using (var unsubscribeTimeout = NewTimeoutToken(cancellationToken))
- {
- var unsubscribeResult = await InternalClient.UnsubscribeAsync(unsubscribeOptionsBuilder.Build(), unsubscribeTimeout.Token).ConfigureAwait(false);
- unsubscribeResults.Add(unsubscribeResult);
- }
-
- //clear because these worked, maybe the subscribe below will fail, only report those
- removedSubscriptions.Clear();
- }
-
- if (addedSubscriptions != null && addedSubscriptions.Any())
- {
- var subscribeOptionsBuilder = new MqttClientSubscribeOptionsBuilder();
-
- foreach (var addedSubscription in addedSubscriptions)
- {
- subscribeOptionsBuilder.WithTopicFilter(addedSubscription);
- }
-
- using (var subscribeTimeout = NewTimeoutToken(cancellationToken))
- {
- var subscribeResult = await InternalClient.SubscribeAsync(subscribeOptionsBuilder.Build(), subscribeTimeout.Token).ConfigureAwait(false);
- subscribeResults.Add(subscribeResult);
- }
- }
- }
- catch (Exception exception)
- {
- await HandleSubscriptionExceptionAsync(exception, addedSubscriptions, removedSubscriptions).ConfigureAwait(false);
- }
-
- return new SendSubscribeUnsubscribeResult(subscribeResults, unsubscribeResults);
- }
-
- void StartPublishing()
- {
- StopPublishing();
-
- var cancellationTokenSource = new CancellationTokenSource();
- var cancellationToken = cancellationTokenSource.Token;
- _publishingCancellationToken = cancellationTokenSource;
-
- Task.Run(() => PublishQueuedMessagesAsync(cancellationToken), cancellationToken).RunInBackground(_logger);
- }
-
- void StopMaintainingConnection()
- {
- try
- {
- _connectionCancellationToken?.Cancel(false);
- }
- finally
- {
- _connectionCancellationToken?.Dispose();
- _connectionCancellationToken = null;
- }
- }
-
- void StopPublishing()
- {
- try
- {
- _publishingCancellationToken?.Cancel(false);
- }
- finally
- {
- _publishingCancellationToken?.Dispose();
- _publishingCancellationToken = null;
- }
- }
-
- async Task TryMaintainConnectionAsync(CancellationToken cancellationToken)
- {
- try
- {
- var oldConnectionState = InternalClient.IsConnected;
- var connectionState = await ReconnectIfRequiredAsync(cancellationToken).ConfigureAwait(false);
-
- if (connectionState == ReconnectionResult.NotConnected)
- {
- StopPublishing();
- await Task.Delay(Options.AutoReconnectDelay, cancellationToken).ConfigureAwait(false);
- }
- else if (connectionState == ReconnectionResult.Reconnected)
- {
- await PublishReconnectSubscriptionsAsync(cancellationToken).ConfigureAwait(false);
- StartPublishing();
- }
- else if (connectionState == ReconnectionResult.Recovered)
- {
- StartPublishing();
- }
- else if (connectionState == ReconnectionResult.StillConnected)
- {
- await PublishSubscriptionsAsync(Options.ConnectionCheckInterval, cancellationToken).ConfigureAwait(false);
- }
-
- if (oldConnectionState != InternalClient.IsConnected)
- {
- await _connectionStateChangedEvent.InvokeAsync(EventArgs.Empty).ConfigureAwait(false);
- }
- }
- catch (OperationCanceledException)
- {
- }
- catch (MqttCommunicationException exception)
- {
- _logger.Warning(exception, "Communication error while maintaining connection.");
- }
- catch (Exception exception)
- {
- _logger.Error(exception, "Error exception while maintaining connection.");
- }
- }
-
- async Task TryPublishQueuedMessageAsync(ManagedMqttApplicationMessage message, CancellationToken cancellationToken)
- {
- Exception transmitException = null;
- bool acceptPublish = true;
- try
- {
- if (_interceptingPublishMessageEvent.HasHandlers)
- {
- var interceptEventArgs = new InterceptingPublishMessageEventArgs(message);
- await _interceptingPublishMessageEvent.InvokeAsync(interceptEventArgs).ConfigureAwait(false);
- acceptPublish = interceptEventArgs.AcceptPublish;
- }
-
- if (acceptPublish)
- {
- using (var publishTimeout = NewTimeoutToken(cancellationToken))
- {
- await InternalClient.PublishAsync(message.ApplicationMessage, publishTimeout.Token).ConfigureAwait(false);
- }
- }
-
- using (await _messageQueueLock.EnterAsync().ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
- {
- // While publishing this message, this.PublishAsync could have booted this
- // message off the queue to make room for another (when using a cap
- // with the DropOldestQueuedMessage strategy). If the first item
- // in the queue is equal to this message, then it's safe to remove
- // it from the queue. If not, that means this.PublishAsync has already
- // removed it, in which case we don't want to do anything.
- _messageQueue.RemoveFirst(i => i.Id.Equals(message.Id));
-
- if (_storageManager != null)
- {
- await _storageManager.RemoveAsync(message).ConfigureAwait(false);
- }
- }
- }
- catch (MqttCommunicationException exception)
- {
- transmitException = exception;
-
- _logger.Warning(exception, "Publishing application message ({0}) failed.", message.Id);
-
- if (message.ApplicationMessage.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
- {
- //If QoS 0, we don't want this message to stay on the queue.
- //If QoS 1 or 2, it's possible that, when using a cap, this message
- //has been booted off the queue by this.PublishAsync, in which case this
- //thread will not continue to try to publish it. While this does
- //contradict the expected behavior of QoS 1 and 2, that's also true
- //for the usage of a message queue cap, so it's still consistent
- //with prior behavior in that way.
- using (await _messageQueueLock.EnterAsync().ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
- {
- _messageQueue.RemoveFirst(i => i.Id.Equals(message.Id));
-
- if (_storageManager != null)
- {
- await _storageManager.RemoveAsync(message).ConfigureAwait(false);
- }
- }
- }
- }
- catch (Exception exception)
- {
- transmitException = exception;
- _logger.Error(exception, "Error while publishing application message ({0}).", message.Id);
- }
- finally
- {
- if (_applicationMessageProcessedEvent.HasHandlers)
- {
- var eventArgs = new ApplicationMessageProcessedEventArgs(message, transmitException);
- await _applicationMessageProcessedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientExtensions.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientExtensions.cs
deleted file mode 100644
index 54dd39e7b..000000000
--- a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientExtensions.cs
+++ /dev/null
@@ -1,78 +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 System.Threading.Tasks;
-using MQTTnet.Packets;
-using MQTTnet.Protocol;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public static class ManagedMqttClientExtensions
- {
- public static Task EnqueueAsync(
- this IManagedMqttClient managedMqttClient,
- string topic,
- string payload = null,
- MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce,
- bool retain = false)
- {
- if (managedMqttClient == null)
- {
- throw new ArgumentNullException(nameof(managedMqttClient));
- }
-
- if (topic == null)
- {
- throw new ArgumentNullException(nameof(topic));
- }
-
- var applicationMessage = new MqttApplicationMessageBuilder().WithTopic(topic)
- .WithPayload(payload)
- .WithRetainFlag(retain)
- .WithQualityOfServiceLevel(qualityOfServiceLevel)
- .Build();
-
- return managedMqttClient.EnqueueAsync(applicationMessage);
- }
-
- public static Task SubscribeAsync(
- this IManagedMqttClient managedMqttClient,
- string topic,
- MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce)
- {
- if (managedMqttClient == null)
- {
- throw new ArgumentNullException(nameof(managedMqttClient));
- }
-
- if (topic == null)
- {
- throw new ArgumentNullException(nameof(topic));
- }
-
- return managedMqttClient.SubscribeAsync(
- new List
- {
- new MqttTopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build()
- });
- }
-
- public static Task UnsubscribeAsync(this IManagedMqttClient managedMqttClient, string topic)
- {
- if (managedMqttClient == null)
- {
- throw new ArgumentNullException(nameof(managedMqttClient));
- }
-
- if (topic == null)
- {
- throw new ArgumentNullException(nameof(topic));
- }
-
- return managedMqttClient.UnsubscribeAsync(new List { topic });
- }
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptions.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptions.cs
deleted file mode 100644
index 81bf3b93c..000000000
--- a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptions.cs
+++ /dev/null
@@ -1,31 +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 MQTTnet.Client;
-using MQTTnet.Server;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public sealed class ManagedMqttClientOptions
- {
- public MqttClientOptions ClientOptions { get; set; }
-
- public TimeSpan AutoReconnectDelay { get; set; } = TimeSpan.FromSeconds(5);
-
- public TimeSpan ConnectionCheckInterval { get; set; } = TimeSpan.FromSeconds(1);
-
- public IManagedMqttClientStorage Storage { get; set; }
-
- public int MaxPendingMessages { get; set; } = int.MaxValue;
-
- public MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; set; } = MqttPendingMessagesOverflowStrategy.DropNewMessage;
-
- ///
- /// Defines the maximum amount of topic filters which will be sent in a SUBSCRIBE/UNSUBSCRIBE packet.
- /// Amazon Web Services (AWS) limits this number to 8. The default is int.MaxValue.
- ///
- public int MaxTopicFiltersInSubscribeUnsubscribePackets { get; set; } = int.MaxValue;
- }
-}
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptionsBuilder.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptionsBuilder.cs
deleted file mode 100644
index 72c192b50..000000000
--- a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptionsBuilder.cs
+++ /dev/null
@@ -1,97 +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 MQTTnet.Client;
-using MQTTnet.Server;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public class ManagedMqttClientOptionsBuilder
- {
- readonly ManagedMqttClientOptions _options = new ManagedMqttClientOptions();
- MqttClientOptionsBuilder _clientOptionsBuilder;
-
- public ManagedMqttClientOptionsBuilder WithMaxPendingMessages(int value)
- {
- _options.MaxPendingMessages = value;
- return this;
- }
-
- public ManagedMqttClientOptionsBuilder WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy value)
- {
- _options.PendingMessagesOverflowStrategy = value;
- return this;
- }
-
- public ManagedMqttClientOptionsBuilder WithAutoReconnectDelay(TimeSpan value)
- {
- _options.AutoReconnectDelay = value;
- return this;
- }
-
- public ManagedMqttClientOptionsBuilder WithStorage(IManagedMqttClientStorage value)
- {
- _options.Storage = value;
- return this;
- }
-
- public ManagedMqttClientOptionsBuilder WithClientOptions(MqttClientOptions value)
- {
- if (_clientOptionsBuilder != null)
- {
- throw new InvalidOperationException("Cannot use client options builder and client options at the same time.");
- }
-
- _options.ClientOptions = value ?? throw new ArgumentNullException(nameof(value));
-
- return this;
- }
-
- public ManagedMqttClientOptionsBuilder WithClientOptions(MqttClientOptionsBuilder builder)
- {
- if (_options.ClientOptions != null)
- {
- throw new InvalidOperationException("Cannot use client options builder and client options at the same time.");
- }
-
- _clientOptionsBuilder = builder;
- return this;
- }
-
- public ManagedMqttClientOptionsBuilder WithClientOptions(Action options)
- {
- if (options == null) throw new ArgumentNullException(nameof(options));
-
- if (_clientOptionsBuilder == null)
- {
- _clientOptionsBuilder = new MqttClientOptionsBuilder();
- }
-
- options(_clientOptionsBuilder);
- return this;
- }
-
- public ManagedMqttClientOptionsBuilder WithMaxTopicFiltersInSubscribeUnsubscribePackets(int value)
- {
- _options.MaxTopicFiltersInSubscribeUnsubscribePackets = value;
- return this;
- }
-
- public ManagedMqttClientOptions Build()
- {
- if (_clientOptionsBuilder != null)
- {
- _options.ClientOptions = _clientOptionsBuilder.Build();
- }
-
- if (_options.ClientOptions == null)
- {
- throw new InvalidOperationException("The ClientOptions cannot be null.");
- }
-
- return _options;
- }
- }
-}
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientStorageManager.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientStorageManager.cs
deleted file mode 100644
index bd1c48c20..000000000
--- a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientStorageManager.cs
+++ /dev/null
@@ -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 System.Threading;
-using System.Threading.Tasks;
-using MQTTnet.Internal;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public class ManagedMqttClientStorageManager
- {
- private readonly List _messages = new List();
- private readonly AsyncLock _messagesLock = new AsyncLock();
-
- private readonly IManagedMqttClientStorage _storage;
-
- public ManagedMqttClientStorageManager(IManagedMqttClientStorage storage)
- {
- _storage = storage ?? throw new ArgumentNullException(nameof(storage));
- }
-
- public async Task> LoadQueuedMessagesAsync()
- {
- var loadedMessages = await _storage.LoadQueuedMessagesAsync().ConfigureAwait(false);
- _messages.AddRange(loadedMessages);
-
- return _messages;
- }
-
- public async Task AddAsync(ManagedMqttApplicationMessage applicationMessage)
- {
- if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
-
- using (await _messagesLock.EnterAsync().ConfigureAwait(false))
- {
- _messages.Add(applicationMessage);
- await SaveAsync().ConfigureAwait(false);
- }
- }
-
- public async Task RemoveAsync(ManagedMqttApplicationMessage applicationMessage)
- {
- if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
-
- using (await _messagesLock.EnterAsync().ConfigureAwait(false))
- {
- var index = _messages.IndexOf(applicationMessage);
- if (index == -1)
- {
- return;
- }
-
- _messages.RemoveAt(index);
- await SaveAsync().ConfigureAwait(false);
- }
- }
-
- private Task SaveAsync()
- {
- return _storage.SaveQueuedMessagesAsync(_messages);
- }
- }
-}
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedProcessFailedEventArgs.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedProcessFailedEventArgs.cs
deleted file mode 100644
index 937c7d83a..000000000
--- a/Source/MQTTnet.Extensions.ManagedClient/ManagedProcessFailedEventArgs.cs
+++ /dev/null
@@ -1,34 +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 MQTTnet.Packets;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public class ManagedProcessFailedEventArgs : EventArgs
- {
- public ManagedProcessFailedEventArgs(Exception exception, List addedSubscriptions, List removedSubscriptions)
- {
- Exception = exception ?? throw new ArgumentNullException(nameof(exception));
-
- if (addedSubscriptions != null)
- {
- AddedSubscriptions = new List(addedSubscriptions.Select(item => item.Topic));
- }
-
- if (removedSubscriptions != null)
- {
- RemovedSubscriptions = new List(removedSubscriptions);
- }
- }
-
- public Exception Exception { get; }
-
- public List AddedSubscriptions { get; }
- public List RemovedSubscriptions { get; }
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.ManagedClient/MqttFactoryExtensions.cs b/Source/MQTTnet.Extensions.ManagedClient/MqttFactoryExtensions.cs
deleted file mode 100644
index f5b9a7335..000000000
--- a/Source/MQTTnet.Extensions.ManagedClient/MqttFactoryExtensions.cs
+++ /dev/null
@@ -1,48 +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 MQTTnet.Client;
-using MQTTnet.Diagnostics;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public static class MqttFactoryExtensions
- {
- public static IManagedMqttClient CreateManagedMqttClient(this MqttFactory factory, IMqttClient mqttClient = null)
- {
- if (factory == null)
- {
- throw new ArgumentNullException(nameof(factory));
- }
-
- if (mqttClient == null)
- {
- return new ManagedMqttClient(factory.CreateMqttClient(), factory.DefaultLogger);
- }
-
- return new ManagedMqttClient(mqttClient, factory.DefaultLogger);
- }
-
- public static IManagedMqttClient CreateManagedMqttClient(this MqttFactory factory, IMqttNetLogger logger)
- {
- if (factory == null)
- {
- throw new ArgumentNullException(nameof(factory));
- }
-
- if (logger == null)
- {
- throw new ArgumentNullException(nameof(logger));
- }
-
- return new ManagedMqttClient(factory.CreateMqttClient(logger), logger);
- }
-
- public static ManagedMqttClientOptionsBuilder CreateManagedMqttClientOptionsBuilder(this MqttFactory _)
- {
- return new ManagedMqttClientOptionsBuilder();
- }
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.ManagedClient/SendSubscribeUnsubscribeResult.cs b/Source/MQTTnet.Extensions.ManagedClient/SendSubscribeUnsubscribeResult.cs
deleted file mode 100644
index 11127f54f..000000000
--- a/Source/MQTTnet.Extensions.ManagedClient/SendSubscribeUnsubscribeResult.cs
+++ /dev/null
@@ -1,23 +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.Client;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public sealed class SendSubscribeUnsubscribeResult
- {
- public SendSubscribeUnsubscribeResult(List subscribeResults, List unsubscribeResults)
- {
- SubscribeResults = subscribeResults ?? throw new ArgumentNullException(nameof(subscribeResults));
- UnsubscribeResults = unsubscribeResults ?? throw new ArgumentNullException(nameof(unsubscribeResults));
- }
-
- public List SubscribeResults { get; private set; }
-
- public List UnsubscribeResults { get; private set; }
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.ManagedClient/SubscriptionsChangedEventArgs.cs b/Source/MQTTnet.Extensions.ManagedClient/SubscriptionsChangedEventArgs.cs
deleted file mode 100644
index 3118001f6..000000000
--- a/Source/MQTTnet.Extensions.ManagedClient/SubscriptionsChangedEventArgs.cs
+++ /dev/null
@@ -1,23 +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 MQTTnet.Client;
-using System;
-using System.Collections.Generic;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public sealed class SubscriptionsChangedEventArgs : EventArgs
- {
- public SubscriptionsChangedEventArgs(List subscribeResult, List unsubscribeResult)
- {
- SubscribeResult = subscribeResult ?? throw new ArgumentNullException(nameof(subscribeResult));
- UnsubscribeResult = unsubscribeResult ?? throw new ArgumentNullException(nameof(unsubscribeResult));
- }
-
- public List SubscribeResult { get; }
-
- public List UnsubscribeResult { get; }
- }
-}
diff --git a/Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/DefaultMqttRpcClientTopicGenerationStrategy.cs b/Source/MQTTnet.Extensions.Rpc/DefaultMqttRpcClientTopicGenerationStrategy.cs
similarity index 100%
rename from Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/DefaultMqttRpcClientTopicGenerationStrategy.cs
rename to Source/MQTTnet.Extensions.Rpc/DefaultMqttRpcClientTopicGenerationStrategy.cs
diff --git a/Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/IMqttRpcClientTopicGenerationStrategy.cs b/Source/MQTTnet.Extensions.Rpc/IMqttRpcClientTopicGenerationStrategy.cs
similarity index 100%
rename from Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/IMqttRpcClientTopicGenerationStrategy.cs
rename to Source/MQTTnet.Extensions.Rpc/IMqttRpcClientTopicGenerationStrategy.cs
diff --git a/Source/MQTTnet.Extensions.Rpc/MQTTnet.Extensions.Rpc.csproj b/Source/MQTTnet.Extensions.Rpc/MQTTnet.Extensions.Rpc.csproj
index 9a21ac89c..b5b8eef92 100644
--- a/Source/MQTTnet.Extensions.Rpc/MQTTnet.Extensions.Rpc.csproj
+++ b/Source/MQTTnet.Extensions.Rpc/MQTTnet.Extensions.Rpc.csproj
@@ -1,10 +1,7 @@
- netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0
- $(TargetFrameworks);net452;net461;net48
- $(TargetFrameworks);uap10.0
-
+ net8.0
MQTTnet.Extensions.Rpc
MQTTnet.Extensions.Rpc
True
@@ -33,21 +30,11 @@
For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).
true
true
- 1591;NETSDK1138
- 7.3
-
-
-
- false
- UAP,Version=v10.0
- UAP
- 10.0.18362.0
- 10.0.10240.0
- .NETCore
- v5.0
- $(DefineConstants);WINDOWS_UWP
- en
- $(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets
+ 1591;NETSDK1138;NU1803;NU1901;NU1902
+ true
+ all
+ true
+ latest-Recommended
@@ -56,13 +43,13 @@
\
-
+
-
+
-
+
diff --git a/Source/MQTTnet.Extensions.Rpc/MQTTnet.Extensions.Rpc.csproj.DotSettings b/Source/MQTTnet.Extensions.Rpc/MQTTnet.Extensions.Rpc.csproj.DotSettings
index 1b491ba9f..18d916ce4 100644
--- a/Source/MQTTnet.Extensions.Rpc/MQTTnet.Extensions.Rpc.csproj.DotSettings
+++ b/Source/MQTTnet.Extensions.Rpc/MQTTnet.Extensions.Rpc.csproj.DotSettings
@@ -1,3 +1,7 @@
-
- True
- True
\ No newline at end of file
+
+ True
+ True
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.Rpc/MqttFactoryExtensions.cs b/Source/MQTTnet.Extensions.Rpc/MqttFactoryExtensions.cs
index a88184611..09d690cc3 100644
--- a/Source/MQTTnet.Extensions.Rpc/MqttFactoryExtensions.cs
+++ b/Source/MQTTnet.Extensions.Rpc/MqttFactoryExtensions.cs
@@ -3,15 +3,14 @@
// See the LICENSE file in the project root for more information.
using System;
-using MQTTnet.Client;
namespace MQTTnet.Extensions.Rpc
{
public static class MqttFactoryExtensions
{
- public static IMqttRpcClient CreateMqttRpcClient(this MqttFactory factory, IMqttClient mqttClient)
+ public static IMqttRpcClient CreateMqttRpcClient(this MqttClientFactory clientFactory, IMqttClient mqttClient)
{
- return factory.CreateMqttRpcClient(
+ return clientFactory.CreateMqttRpcClient(
mqttClient,
new MqttRpcClientOptions
{
@@ -19,7 +18,7 @@ public static IMqttRpcClient CreateMqttRpcClient(this MqttFactory factory, IMqtt
});
}
- public static IMqttRpcClient CreateMqttRpcClient(this MqttFactory _, IMqttClient mqttClient, MqttRpcClientOptions rpcClientOptions)
+ public static IMqttRpcClient CreateMqttRpcClient(this MqttClientFactory _, IMqttClient mqttClient, MqttRpcClientOptions rpcClientOptions)
{
if (mqttClient == null)
{
diff --git a/Source/MQTTnet.Extensions.Rpc/MqttRpcClient.cs b/Source/MQTTnet.Extensions.Rpc/MqttRpcClient.cs
index 663e23846..3a301690e 100644
--- a/Source/MQTTnet.Extensions.Rpc/MqttRpcClient.cs
+++ b/Source/MQTTnet.Extensions.Rpc/MqttRpcClient.cs
@@ -3,12 +3,12 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using MQTTnet.Client;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Internal;
@@ -132,7 +132,7 @@ Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventAr
return CompletedTask.Instance;
}
- var payloadBuffer = eventArgs.ApplicationMessage.PayloadSegment.ToArray();
+ var payloadBuffer = eventArgs.ApplicationMessage.Payload.ToArray();
awaitable.TrySetResult(payloadBuffer);
// Set this message to handled to that other code can avoid execution etc.
diff --git a/Source/MQTTnet.Extensions.Rpc/Options/MqttRpcClientOptions.cs b/Source/MQTTnet.Extensions.Rpc/MqttRpcClientOptions.cs
similarity index 100%
rename from Source/MQTTnet.Extensions.Rpc/Options/MqttRpcClientOptions.cs
rename to Source/MQTTnet.Extensions.Rpc/MqttRpcClientOptions.cs
diff --git a/Source/MQTTnet.Extensions.Rpc/Options/MqttRpcClientOptionsBuilder.cs b/Source/MQTTnet.Extensions.Rpc/MqttRpcClientOptionsBuilder.cs
similarity index 100%
rename from Source/MQTTnet.Extensions.Rpc/Options/MqttRpcClientOptionsBuilder.cs
rename to Source/MQTTnet.Extensions.Rpc/MqttRpcClientOptionsBuilder.cs
diff --git a/Source/MQTTnet.Extensions.Rpc/Options/MqttRpcTopicPair.cs b/Source/MQTTnet.Extensions.Rpc/MqttRpcTopicPair.cs
similarity index 100%
rename from Source/MQTTnet.Extensions.Rpc/Options/MqttRpcTopicPair.cs
rename to Source/MQTTnet.Extensions.Rpc/MqttRpcTopicPair.cs
diff --git a/Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/TopicGenerationContext.cs b/Source/MQTTnet.Extensions.Rpc/TopicGenerationContext.cs
similarity index 98%
rename from Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/TopicGenerationContext.cs
rename to Source/MQTTnet.Extensions.Rpc/TopicGenerationContext.cs
index a556825a5..c40544d26 100644
--- a/Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/TopicGenerationContext.cs
+++ b/Source/MQTTnet.Extensions.Rpc/TopicGenerationContext.cs
@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
-using MQTTnet.Client;
using MQTTnet.Protocol;
namespace MQTTnet.Extensions.Rpc
diff --git a/Source/MQTTnet.Extensions.TopicTemplate/MQTTnet.Extensions.TopicTemplate.csproj b/Source/MQTTnet.Extensions.TopicTemplate/MQTTnet.Extensions.TopicTemplate.csproj
index 14d653c04..adc6c0069 100644
--- a/Source/MQTTnet.Extensions.TopicTemplate/MQTTnet.Extensions.TopicTemplate.csproj
+++ b/Source/MQTTnet.Extensions.TopicTemplate/MQTTnet.Extensions.TopicTemplate.csproj
@@ -1,10 +1,7 @@
- netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0
- $(TargetFrameworks);net452;net461;net48
- $(TargetFrameworks);uap10.0
-
+ net8.0
MQTTnet.Extensions.TopicTemplate
MQTTnet.Extensions.TopicTemplate
True
@@ -37,19 +34,6 @@
1591;NETSDK1138
-
- false
- UAP,Version=v10.0
- UAP
- 10.0.18362.0
- 10.0.10240.0
- .NETCore
- v5.0
- $(DefineConstants);WINDOWS_UWP
- en
- $(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets
-
-
True
@@ -60,7 +44,7 @@
\
-
+
diff --git a/Source/MQTTnet.Extensions.TopicTemplate/MqttTopicTemplate.cs b/Source/MQTTnet.Extensions.TopicTemplate/MqttTopicTemplate.cs
index f5dc48f11..25b3f3eaa 100644
--- a/Source/MQTTnet.Extensions.TopicTemplate/MqttTopicTemplate.cs
+++ b/Source/MQTTnet.Extensions.TopicTemplate/MqttTopicTemplate.cs
@@ -9,349 +9,347 @@
using System.Threading;
using MQTTnet.Protocol;
-namespace MQTTnet.Extensions.TopicTemplate
+namespace MQTTnet.Extensions.TopicTemplate;
+
+///
+/// A topic template is an MQTT topic filter string that may contain
+/// segments in curly braces called parameters. This well-known
+/// 'moustache' syntax also matches AsyncAPI Channel Address Expressions.
+/// The topic template is designed to support dynamic subscription/publication,
+/// message-topic matching and routing. It is intended to be more safe and
+/// convenient than String.Format() for aforementioned purposes.
+///
+///
+/// topic/subtopic/{parameter}/{otherParameter}
+///
+public sealed class MqttTopicTemplate : IEquatable
{
+ static readonly Regex MoustacheRegex = new("{([^/]+?)}", RegexOptions.Compiled);
+
+ readonly string[] _parameterSegments;
+
+ string _topicFilter;
+
///
- /// A topic template is an MQTT topic filter string that may contain
- /// segments in curly braces called parameters. This well-known
- /// 'moustache' syntax also matches AsyncAPI Channel Address Expressions.
- /// The topic template is designed to support dynamic subscription/publication,
- /// message-topic matching and routing. It is intended to be more safe and
- /// convenient than String.Format() for aforementioned purposes.
+ /// Create a topic template from an mqtt topic filter with moustache placeholders.
///
- ///
- /// topic/subtopic/{parameter}/{otherParameter}
- ///
- public sealed class MqttTopicTemplate : IEquatable
+ ///
+ ///
+ ///
+ ///
+ public MqttTopicTemplate(string topicTemplate)
{
- static readonly Regex MoustacheRegex = new Regex("{([^/]+?)}", RegexOptions.Compiled);
-
- readonly string[] _parameterSegments;
-
- string _topicFilter;
-
- ///
- /// Create a topic template from an mqtt topic filter with moustache placeholders.
- ///
- ///
- ///
- ///
- ///
- public MqttTopicTemplate(string topicTemplate)
+ if (topicTemplate == null)
{
- if (topicTemplate == null)
- {
- throw new ArgumentNullException(nameof(topicTemplate));
- }
-
- MqttTopicValidator.ThrowIfInvalidSubscribe(topicTemplate);
-
- Template = topicTemplate;
- _parameterSegments = topicTemplate.Split(MqttTopicFilterComparer.LevelSeparator)
- .Select(segment => MoustacheRegex.Match(segment).Groups[1].Value)
- .Select(s => s.Length > 0 ? s : null)
- .ToArray();
+ throw new ArgumentNullException(nameof(topicTemplate));
}
-
- ///
- /// Yield the template parameter names.
- ///
- public IEnumerable Parameters => _parameterSegments.Where(s => s != null);
-
- ///
- /// The topic template string representation, e.g. A/B/{foo}/D.
- ///
- public string Template { get; }
-
- ///
- /// The topic template as an MQTT topic filter (+ substituted for all parameters). If the template
- /// ends with a multi-level wildcard (hash), this will be reflected here.
- ///
- public string TopicFilter
+
+ MqttTopicValidator.ThrowIfInvalidSubscribe(topicTemplate);
+
+ Template = topicTemplate;
+ _parameterSegments = topicTemplate.Split(MqttTopicFilterComparer.LevelSeparator)
+ .Select(segment => MoustacheRegex.Match(segment).Groups[1].Value)
+ .Select(s => s.Length > 0 ? s : null)
+ .ToArray();
+ }
+
+ ///
+ /// Yield the template parameter names.
+ ///
+ public IEnumerable Parameters => _parameterSegments.Where(s => s != null);
+
+ ///
+ /// The topic template string representation, e.g. A/B/{foo}/D.
+ ///
+ public string Template { get; }
+
+ ///
+ /// The topic template as an MQTT topic filter (+ substituted for all parameters). If the template
+ /// ends with a multi-level wildcard (hash), this will be reflected here.
+ ///
+ public string TopicFilter
+ {
+ get
{
- get
- {
- LazyInitializer.EnsureInitialized(ref _topicFilter, () => MoustacheRegex.Replace(Template, MqttTopicFilterComparer.SingleLevelWildcard.ToString()));
- return _topicFilter;
- }
+ LazyInitializer.EnsureInitialized(ref _topicFilter, () => MoustacheRegex.Replace(Template, MqttTopicFilterComparer.SingleLevelWildcard.ToString()));
+ return _topicFilter;
}
-
- ///
- /// Return the topic filter of this template, ending with a multi-level wildcard (hash).
- ///
- public string TopicTreeRootFilter
+ }
+
+ ///
+ /// Return the topic filter of this template, ending with a multi-level wildcard (hash).
+ ///
+ public string TopicTreeRootFilter
+ {
+ get
{
- get
+ var filter = TopicFilter;
+ // append slash if neccessary
+ if (filter.Length > 0 && !filter.EndsWith(MqttTopicFilterComparer.LevelSeparator.ToString()) && !filter.EndsWith(MqttTopicFilterComparer.MultiLevelWildcard.ToString()))
{
- var filter = TopicFilter;
- // append slash if neccessary
- if (filter.Length > 0 && !filter.EndsWith(MqttTopicFilterComparer.LevelSeparator.ToString()) &&
- !filter.EndsWith(MqttTopicFilterComparer.MultiLevelWildcard.ToString()))
- {
- filter += MqttTopicFilterComparer.LevelSeparator;
- }
-
- // append hash if neccessary
- if (!filter.EndsWith(MqttTopicFilterComparer.MultiLevelWildcard.ToString()))
- {
- filter += MqttTopicFilterComparer.MultiLevelWildcard;
- }
-
- return filter;
+ filter += MqttTopicFilterComparer.LevelSeparator;
+ }
+
+ // append hash if neccessary
+ if (!filter.EndsWith(MqttTopicFilterComparer.MultiLevelWildcard.ToString()))
+ {
+ filter += MqttTopicFilterComparer.MultiLevelWildcard;
}
+
+ return filter;
}
-
- public bool Equals(MqttTopicTemplate other)
+ }
+
+ public bool Equals(MqttTopicTemplate other)
+ {
+ return other != null && Template == other.Template;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj))
{
- return other != null && Template == other.Template;
+ return false;
}
-
- public override bool Equals(object obj)
+
+ if (ReferenceEquals(this, obj))
{
- if (ReferenceEquals(null, obj))
- {
- return false;
- }
-
- if (ReferenceEquals(this, obj))
- {
- return true;
- }
-
- if (obj.GetType() != GetType())
- {
- return false;
- }
-
- return Equals((MqttTopicTemplate)obj);
+ return true;
}
-
- ///
- /// Determine the shortest common prefix of the given templates. Partial segments
- /// are not returned.
- ///
- ///
- /// topic templates
- ///
- ///
- public static MqttTopicTemplate FindCanonicalPrefix(IEnumerable templates)
+
+ if (obj.GetType() != GetType())
{
- string root = null;
-
- string CommonPrefix(string a, string b)
+ return false;
+ }
+
+ return Equals((MqttTopicTemplate)obj);
+ }
+
+ ///
+ /// Determine the shortest common prefix of the given templates. Partial segments
+ /// are not returned.
+ ///
+ ///
+ /// topic templates
+ ///
+ ///
+ public static MqttTopicTemplate FindCanonicalPrefix(IEnumerable templates)
+ {
+ string root = null;
+
+ string CommonPrefix(string a, string b)
+ {
+ var maxIndex = Math.Min(a.Length, b.Length) - 1;
+ for (var i = 0; i <= maxIndex; i++)
{
- var maxIndex = Math.Min(a.Length, b.Length) - 1;
- for (var i = 0; i <= maxIndex; i++)
+ if (a[i] != b[i])
{
- if (a[i] != b[i])
- {
- return a.Substring(0, i);
- }
+ return a.Substring(0, i);
}
-
- return a.Substring(0, maxIndex+1);
- }
-
- foreach (string topic in from template in templates select template.Template)
- {
- root = root == null ? topic : CommonPrefix(root, topic);
- }
-
- if (string.IsNullOrEmpty(root))
- return new MqttTopicTemplate(MqttTopicFilterComparer.MultiLevelWildcard.ToString());
-
- if (root.Contains(MqttTopicFilterComparer.LevelSeparator) &&
- !root.EndsWith(MqttTopicFilterComparer.LevelSeparator.ToString()) &&
- !root.EndsWith("}"))
- {
- root = root.Substring(0, root.LastIndexOf(MqttTopicFilterComparer.LevelSeparator)+1);
}
-
- if (root.EndsWith(MqttTopicFilterComparer.LevelSeparator.ToString()))
- root += MqttTopicFilterComparer.SingleLevelWildcard;
- return new MqttTopicTemplate(root);
+ return a.Substring(0, maxIndex + 1);
}
-
- public override int GetHashCode()
+
+ foreach (var topic in from template in templates select template.Template)
{
- return Template.GetHashCode();
+ root = root == null ? topic : CommonPrefix(root, topic);
}
-
- ///
- /// Test if this topic template matches a given topic.
- ///
- ///
- /// a fully specified topic
- ///
- ///
- /// true to match including the subtree (multi-level wildcard)
- ///
- /// true iff the topic matches the template's filter
- ///
- ///
- ///
- /// if the topic is invalid
- ///
- public bool MatchesTopic(string topic, bool subtree = false)
+
+ if (string.IsNullOrEmpty(root))
{
- var comparison = MqttTopicFilterComparer.Compare(topic, subtree ? TopicTreeRootFilter : TopicFilter);
- if (comparison == MqttTopicFilterCompareResult.FilterInvalid)
- {
- throw new InvalidOperationException("Invalid filter");
- }
-
- if (comparison == MqttTopicFilterCompareResult.TopicInvalid)
- {
- throw new ArgumentException("Invalid topic", nameof(topic));
- }
-
- return comparison == MqttTopicFilterCompareResult.IsMatch;
+ return new MqttTopicTemplate(MqttTopicFilterComparer.MultiLevelWildcard.ToString());
}
-
- ///
- /// Extract the parameter values from a topic corresponding to the template
- /// parameters. The topic has to match this template.
- ///
- ///
- /// the topic
- ///
- /// an enumeration of (parameter, index, value)
- public IEnumerable<(string parameter, int index, string value)> ParseParameterValues(string topic)
+
+ if (root.Contains(MqttTopicFilterComparer.LevelSeparator) && !root.EndsWith(MqttTopicFilterComparer.LevelSeparator.ToString()) && !root.EndsWith("}"))
{
- if (!MatchesTopic(topic))
- {
- throw new ArgumentException("the topic has to match this template", nameof(topic));
- }
-
- return parseParameterValuesInternal(topic);
+ root = root.Substring(0, root.LastIndexOf(MqttTopicFilterComparer.LevelSeparator) + 1);
}
-
- ///
- /// Extract the parameter values from the message topic corresponding to the template
- /// parameters. The message topic has to match this topic template.
- ///
- ///
- /// the message
- ///
- /// an enumeration of (parameter, index, value)
- public IEnumerable<(string parameter, int index, string value)> ParseParameterValues(MqttApplicationMessage message)
+
+ if (root.EndsWith(MqttTopicFilterComparer.LevelSeparator.ToString()))
{
- return ParseParameterValues(message.Topic);
+ root += MqttTopicFilterComparer.SingleLevelWildcard;
}
-
- ///
- /// Try to set a parameter to a given value. If the parameter is not present,
- /// this is returned. The value must not contain slashes.
- ///
- ///
- /// a template parameter
- ///
- ///
- /// a string
- ///
- ///
- public MqttTopicTemplate TrySetParameter(string parameter, string value)
+
+ return new MqttTopicTemplate(root);
+ }
+
+ public override int GetHashCode()
+ {
+ return Template.GetHashCode();
+ }
+
+ ///
+ /// Test if this topic template matches a given topic.
+ ///
+ ///
+ /// a fully specified topic
+ ///
+ ///
+ /// true to match including the subtree (multi-level wildcard)
+ ///
+ /// true iff the topic matches the template's filter
+ ///
+ ///
+ ///
+ /// if the topic is invalid
+ ///
+ public bool MatchesTopic(string topic, bool subtree = false)
+ {
+ var comparison = MqttTopicFilterComparer.Compare(topic, subtree ? TopicTreeRootFilter : TopicFilter);
+ if (comparison == MqttTopicFilterCompareResult.FilterInvalid)
{
- if (parameter != null && _parameterSegments.Contains(parameter))
- {
- return WithParameter(parameter, value);
- }
-
- return this;
+ throw new InvalidOperationException("Invalid filter");
}
-
- ///
- /// Replace the given parameter with a single-level wildcard (plus sign).
- ///
- ///
- /// parameter name
- ///
- /// the topic template (without the parameter)
- public MqttTopicTemplate WithoutParameter(string parameter)
+
+ if (comparison == MqttTopicFilterCompareResult.TopicInvalid)
{
- if (string.IsNullOrEmpty(parameter) || !_parameterSegments.Contains(parameter))
- {
- throw new ArgumentException("topic template parameter must exist.");
- }
-
- return ReplaceInternal(parameter, MqttTopicFilterComparer.SingleLevelWildcard.ToString());
+ throw new ArgumentException("Invalid topic", nameof(topic));
}
-
- ///
- /// Substitute a parameter with a given value, thus removing the parameter. If the parameter is not present,
- /// the method trows. The value must not contain slashes or wildcards.
- ///
- ///
- /// a template parameter
- ///
- ///
- /// a string
- ///
- ///
- /// when the parameter is not present
- ///
- /// the topic template (without the parameter)
- public MqttTopicTemplate WithParameter(string parameter, string value)
+
+ return comparison == MqttTopicFilterCompareResult.IsMatch;
+ }
+
+ ///
+ /// Extract the parameter values from a topic corresponding to the template
+ /// parameters. The topic has to match this template.
+ ///
+ ///
+ /// the topic
+ ///
+ /// an enumeration of (parameter, index, value)
+ public IEnumerable<(string parameter, int index, string value)> ParseParameterValues(string topic)
+ {
+ if (!MatchesTopic(topic))
{
- if (value == null || string.IsNullOrEmpty(parameter) || !_parameterSegments.Contains(parameter) ||
- value.Contains(MqttTopicFilterComparer.LevelSeparator) ||
- value.Contains(MqttTopicFilterComparer.SingleLevelWildcard) ||
- value.Contains(MqttTopicFilterComparer.MultiLevelWildcard))
- {
- throw new ArgumentException("parameter must exist and value must not contain slashes or wildcard.");
- }
-
- return ReplaceInternal(parameter, value);
+ throw new ArgumentException("the topic has to match this template", nameof(topic));
+ }
+
+ return parseParameterValuesInternal(topic);
+ }
+
+ ///
+ /// Extract the parameter values from the message topic corresponding to the template
+ /// parameters. The message topic has to match this topic template.
+ ///
+ ///
+ /// the message
+ ///
+ /// an enumeration of (parameter, index, value)
+ public IEnumerable<(string parameter, int index, string value)> ParseParameterValues(MqttApplicationMessage message)
+ {
+ return ParseParameterValues(message.Topic);
+ }
+
+ ///
+ /// Try to set a parameter to a given value. If the parameter is not present,
+ /// this is returned. The value must not contain slashes.
+ ///
+ ///
+ /// a template parameter
+ ///
+ ///
+ /// a string
+ ///
+ ///
+ public MqttTopicTemplate TrySetParameter(string parameter, string value)
+ {
+ if (parameter != null && _parameterSegments.Contains(parameter))
+ {
+ return WithParameter(parameter, value);
}
-
- private MqttTopicTemplate ReplaceInternal(string parameter, string value)
+
+ return this;
+ }
+
+ ///
+ /// Replace the given parameter with a single-level wildcard (plus sign).
+ ///
+ ///
+ /// parameter name
+ ///
+ /// the topic template (without the parameter)
+ public MqttTopicTemplate WithoutParameter(string parameter)
+ {
+ if (string.IsNullOrEmpty(parameter) || !_parameterSegments.Contains(parameter))
{
- var moustache = "{" + parameter + "}";
- return new MqttTopicTemplate(Template.Replace(moustache, value));
+ throw new ArgumentException("topic template parameter must exist.");
}
-
- ///
- /// Reuse parameters as they are extracted using another topic template on this template
- /// when the parameter name matches. Useful
- /// for compatibility routing.
- ///
- ///
- ///
- ///
- public MqttTopicTemplate WithParameterValuesFrom(IEnumerable<(string parameter, int index, string value)> parameters)
+
+ return ReplaceInternal(parameter, MqttTopicFilterComparer.SingleLevelWildcard.ToString());
+ }
+
+ ///
+ /// Substitute a parameter with a given value, thus removing the parameter. If the parameter is not present,
+ /// the method trows. The value must not contain slashes or wildcards.
+ ///
+ ///
+ /// a template parameter
+ ///
+ ///
+ /// a string
+ ///
+ ///
+ /// when the parameter is not present
+ ///
+ /// the topic template (without the parameter)
+ public MqttTopicTemplate WithParameter(string parameter, string value)
+ {
+ if (value == null || string.IsNullOrEmpty(parameter) || !_parameterSegments.Contains(parameter) || value.Contains(MqttTopicFilterComparer.LevelSeparator) ||
+ value.Contains(MqttTopicFilterComparer.SingleLevelWildcard) || value.Contains(MqttTopicFilterComparer.MultiLevelWildcard))
{
- return parameters.Aggregate(this, (t, p) => t.TrySetParameter(p.parameter, p.value));
+ throw new ArgumentException("parameter must exist and value must not contain slashes or wildcard.");
}
-
- IEnumerable<(string parameter, int index, string value)> parseParameterValuesInternal(string topic)
+
+ return ReplaceInternal(parameter, value);
+ }
+
+ ///
+ /// Reuse parameters as they are extracted using another topic template on this template
+ /// when the parameter name matches. Useful
+ /// for compatibility routing.
+ ///
+ ///
+ ///
+ ///
+ public MqttTopicTemplate WithParameterValuesFrom(IEnumerable<(string parameter, int index, string value)> parameters)
+ {
+ return parameters.Aggregate(this, (t, p) => t.TrySetParameter(p.parameter, p.value));
+ }
+
+ IEnumerable<(string parameter, int index, string value)> parseParameterValuesInternal(string topic)
+ {
+ // because we have a match, we know the segment array is at least the template's length
+ var segments = topic.Split(MqttTopicFilterComparer.LevelSeparator);
+ for (var i = 0; i < _parameterSegments.Length; i++)
{
- // because we have a match, we know the segment array is at least the template's length
- var segments = topic.Split(MqttTopicFilterComparer.LevelSeparator);
- for (var i = 0; i < _parameterSegments.Length; i++)
+ var name = _parameterSegments[i];
+ if (name != null)
{
- var name = _parameterSegments[i];
- if (name != null)
- {
- yield return (name, i, segments[i]);
- }
+ yield return (name, i, segments[i]);
}
}
}
+
+ MqttTopicTemplate ReplaceInternal(string parameter, string value)
+ {
+ var moustache = "{" + parameter + "}";
+ return new MqttTopicTemplate(Template.Replace(moustache, value));
+ }
}
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.TopicTemplate/TopicTemplateExtensions.cs b/Source/MQTTnet.Extensions.TopicTemplate/TopicTemplateExtensions.cs
index a4d49e99a..b45bb0fd2 100644
--- a/Source/MQTTnet.Extensions.TopicTemplate/TopicTemplateExtensions.cs
+++ b/Source/MQTTnet.Extensions.TopicTemplate/TopicTemplateExtensions.cs
@@ -4,187 +4,185 @@
using System;
using System.Linq;
-using MQTTnet.Client;
using MQTTnet.Packets;
using MQTTnet.Protocol;
-namespace MQTTnet.Extensions.TopicTemplate
+namespace MQTTnet.Extensions.TopicTemplate;
+
+public static class TopicTemplateExtensions
{
- public static class TopicTemplateExtensions
+ ///
+ /// Modify this message builder to respond to a given message. The
+ /// message's response topic and correlation data are included
+ /// in the message builder.
+ ///
+ ///
+ /// a message builder
+ ///
+ ///
+ /// a message with a response topic
+ ///
+ /// a message builder
+ ///
+ public static MqttApplicationMessageBuilder AsResponseTo(this MqttApplicationMessageBuilder builder, MqttApplicationMessage message)
{
- ///
- /// Modify this message builder to respond to a given message. The
- /// message's response topic and correlation data are included
- /// in the message builder.
- ///
- ///
- /// a message builder
- ///
- ///
- /// a message with a response topic
- ///
- /// a message builder
- ///
- public static MqttApplicationMessageBuilder AsResponseTo(this MqttApplicationMessageBuilder builder, MqttApplicationMessage message)
+ if (!string.IsNullOrEmpty(message.ResponseTopic))
{
- if (!string.IsNullOrEmpty(message.ResponseTopic))
- {
- throw new ArgumentException("message does not have a response topic");
- }
-
- return builder.WithTopic(message.ResponseTopic).WithCorrelationData(message.CorrelationData);
+ throw new ArgumentException("message does not have a response topic");
}
- ///
- /// Set the filter topic according to the template, with
- /// remaining template parameters substituted by single-level
- /// wildcard.
- ///
- ///
- /// a topic template
- ///
- ///
- /// whether to subscribe to the whole topic tree
- ///
- /// the modified topic filter
- public static MqttTopicFilterBuilder BuildFilter(this MqttTopicTemplate topicTemplate, bool subscribeTreeRoot = false)
- {
- return new MqttTopicFilterBuilder().WithTopicTemplate(topicTemplate, subscribeTreeRoot);
- }
+ return builder.WithTopic(message.ResponseTopic).WithCorrelationData(message.CorrelationData);
+ }
- ///
- /// Create a message builder from this template. The template must not have
- /// remaining parameters.
- ///
- ///
- /// a parameterless topic template
- ///
- /// a new message builder
- ///
- /// if the topic template has parameters
- ///
- public static MqttApplicationMessageBuilder BuildMessage(this MqttTopicTemplate topicTemplate)
- {
- return new MqttApplicationMessageBuilder().WithTopicTemplate(topicTemplate);
- }
-
- ///
- /// Return a message builder to respond to this message. The
- /// message's response topic and correlation data are included
- /// in the response message builder.
- ///
- ///
- /// a message with a response topic
- ///
- /// a message builder
- ///
- public static MqttApplicationMessageBuilder BuildResponse(this MqttApplicationMessage message)
- {
- return new MqttApplicationMessageBuilder().AsResponseTo(message);
- }
+ ///
+ /// Set the filter topic according to the template, with
+ /// remaining template parameters substituted by single-level
+ /// wildcard.
+ ///
+ ///
+ /// a topic template
+ ///
+ ///
+ /// whether to subscribe to the whole topic tree
+ ///
+ /// the modified topic filter
+ public static MqttTopicFilterBuilder BuildFilter(this MqttTopicTemplate topicTemplate, bool subscribeTreeRoot = false)
+ {
+ return new MqttTopicFilterBuilder().WithTopicTemplate(topicTemplate, subscribeTreeRoot);
+ }
- ///
- /// Return whether the message matches the given topic template.
- ///
- ///
- /// a message
- ///
- ///
- /// a topic template
- ///
- ///
- /// whether to include the topic subtree
- ///
- ///
- public static bool MatchesTopicTemplate(this MqttApplicationMessage message, MqttTopicTemplate topicTemplate, bool subtree = false)
- {
- return topicTemplate.MatchesTopic(message.Topic, subtree);
- }
+ ///
+ /// Create a message builder from this template. The template must not have
+ /// remaining parameters.
+ ///
+ ///
+ /// a parameterless topic template
+ ///
+ /// a new message builder
+ ///
+ /// if the topic template has parameters
+ ///
+ public static MqttApplicationMessageBuilder BuildMessage(this MqttTopicTemplate topicTemplate)
+ {
+ return new MqttApplicationMessageBuilder().WithTopicTemplate(topicTemplate);
+ }
- ///
- /// Set the filter topic according to the template, with
- /// template parameters substituted by a single-level
- /// wildcard.
- ///
- ///
- /// a filter builder
- ///
- ///
- /// a topic template
- ///
- ///
- /// whether to subscribe to the whole topic tree
- ///
- /// the modified topic filter
- public static MqttTopicFilterBuilder WithTopicTemplate(this MqttTopicFilterBuilder builder, MqttTopicTemplate topicTemplate, bool subscribeTreeRoot = false)
- {
- return builder.WithTopic(subscribeTreeRoot ? topicTemplate.TopicTreeRootFilter : topicTemplate.TopicFilter);
- }
+ ///
+ /// Return a message builder to respond to this message. The
+ /// message's response topic and correlation data are included
+ /// in the response message builder.
+ ///
+ ///
+ /// a message with a response topic
+ ///
+ /// a message builder
+ ///
+ public static MqttApplicationMessageBuilder BuildResponse(this MqttApplicationMessage message)
+ {
+ return new MqttApplicationMessageBuilder().AsResponseTo(message);
+ }
- ///
- /// Set the subscription to the template's topic filter.
- ///
- /// the builder
- public static MqttClientSubscribeOptionsBuilder WithTopicTemplate(
- this MqttClientSubscribeOptionsBuilder builder,
- MqttTopicTemplate topicTemplate,
- MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce,
- bool noLocal = false,
- bool retainAsPublished = false,
- MqttRetainHandling retainHandling = MqttRetainHandling.SendAtSubscribe)
- {
- return builder.WithTopicFilter(
- new MqttTopicFilter
- {
- Topic = topicTemplate.TopicFilter,
- QualityOfServiceLevel = qualityOfServiceLevel,
- NoLocal = noLocal,
- RetainAsPublished = retainAsPublished,
- RetainHandling = retainHandling
- });
- }
-
- ///
- /// Set the publication topic according to the topic template. The template
- /// must not have remaining (unset) parameters or contain wildcards.
- ///
- ///
- /// a message builder
- ///
- ///
- /// a parameterless topic template
- ///
- /// the modified message builder
- ///
- /// if the topic template has parameters
- ///
- public static MqttApplicationMessageBuilder WithTopicTemplate(this MqttApplicationMessageBuilder builder, MqttTopicTemplate topicTemplate)
- {
- if (topicTemplate.Parameters.Any())
+ ///
+ /// Return whether the message matches the given topic template.
+ ///
+ ///
+ /// a message
+ ///
+ ///
+ /// a topic template
+ ///
+ ///
+ /// whether to include the topic subtree
+ ///
+ ///
+ public static bool MatchesTopicTemplate(this MqttApplicationMessage message, MqttTopicTemplate topicTemplate, bool subtree = false)
+ {
+ return topicTemplate.MatchesTopic(message.Topic, subtree);
+ }
+
+ ///
+ /// Set the filter topic according to the template, with
+ /// template parameters substituted by a single-level
+ /// wildcard.
+ ///
+ ///
+ /// a filter builder
+ ///
+ ///
+ /// a topic template
+ ///
+ ///
+ /// whether to subscribe to the whole topic tree
+ ///
+ /// the modified topic filter
+ public static MqttTopicFilterBuilder WithTopicTemplate(this MqttTopicFilterBuilder builder, MqttTopicTemplate topicTemplate, bool subscribeTreeRoot = false)
+ {
+ return builder.WithTopic(subscribeTreeRoot ? topicTemplate.TopicTreeRootFilter : topicTemplate.TopicFilter);
+ }
+
+ ///
+ /// Set the subscription to the template's topic filter.
+ ///
+ /// the builder
+ public static MqttClientSubscribeOptionsBuilder WithTopicTemplate(
+ this MqttClientSubscribeOptionsBuilder builder,
+ MqttTopicTemplate topicTemplate,
+ MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce,
+ bool noLocal = false,
+ bool retainAsPublished = false,
+ MqttRetainHandling retainHandling = MqttRetainHandling.SendAtSubscribe)
+ {
+ return builder.WithTopicFilter(
+ new MqttTopicFilter
{
- throw new ArgumentException("topic templates must be parameter-less when sending " + topicTemplate.Template);
- }
+ Topic = topicTemplate.TopicFilter,
+ QualityOfServiceLevel = qualityOfServiceLevel,
+ NoLocal = noLocal,
+ RetainAsPublished = retainAsPublished,
+ RetainHandling = retainHandling
+ });
+ }
- MqttTopicValidator.ThrowIfInvalid(topicTemplate.Template);
- return builder.WithTopic(topicTemplate.Template);
+ ///
+ /// Set the publication topic according to the topic template. The template
+ /// must not have remaining (unset) parameters or contain wildcards.
+ ///
+ ///
+ /// a message builder
+ ///
+ ///
+ /// a parameterless topic template
+ ///
+ /// the modified message builder
+ ///
+ /// if the topic template has parameters
+ ///
+ public static MqttApplicationMessageBuilder WithTopicTemplate(this MqttApplicationMessageBuilder builder, MqttTopicTemplate topicTemplate)
+ {
+ if (topicTemplate.Parameters.Any())
+ {
+ throw new ArgumentException("topic templates must be parameter-less when sending " + topicTemplate.Template);
}
+
+ MqttTopicValidator.ThrowIfInvalid(topicTemplate.Template);
+ return builder.WithTopic(topicTemplate.Template);
}
}
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.WebSocket4Net/MQTTnet.Extensions.WebSocket4Net.csproj b/Source/MQTTnet.Extensions.WebSocket4Net/MQTTnet.Extensions.WebSocket4Net.csproj
deleted file mode 100644
index 059c5e109..000000000
--- a/Source/MQTTnet.Extensions.WebSocket4Net/MQTTnet.Extensions.WebSocket4Net.csproj
+++ /dev/null
@@ -1,72 +0,0 @@
-
-
-
- netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0
- $(TargetFrameworks);net452;net461;net48
- $(TargetFrameworks);uap10.0
-
- MQTTnet.Extensions.WebSocket4Net
- MQTTnet.Extensions.WebSocket4Net
- True
- The contributors of MQTTnet
- MQTTnet
- This is an extension library which allows using _WebSocket4Net_ as transport for MQTTnet clients.
- The contributors of MQTTnet
- MQTTnet.Extensions.WebSocket4Net
- false
- false
- true
- true
- snupkg
- Copyright (c) .NET Foundation and Contributors
- https://github.com/dotnet/MQTTnet
- https://github.com/dotnet/MQTTnet.git
- git
- MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor
- en-US
- false
- false
- nuget.png
- true
- true
- MIT
- For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).
- true
- true
- 1591;NETSDK1138
- 7.3
-
-
-
- false
- UAP,Version=v10.0
- UAP
- 10.0.18362.0
- 10.0.10240.0
- .NETCore
- v5.0
- $(DefineConstants);WINDOWS_UWP
- en
- $(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets
-
-
-
-
- True
- \
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Source/MQTTnet.Extensions.WebSocket4Net/MqttFactoryExtensions.cs b/Source/MQTTnet.Extensions.WebSocket4Net/MqttFactoryExtensions.cs
deleted file mode 100644
index 1b056f1b9..000000000
--- a/Source/MQTTnet.Extensions.WebSocket4Net/MqttFactoryExtensions.cs
+++ /dev/null
@@ -1,21 +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;
-
-namespace MQTTnet.Extensions.WebSocket4Net
-{
- public static class MqttFactoryExtensions
- {
- public static MqttFactory UseWebSocket4Net(this MqttFactory mqttFactory)
- {
- if (mqttFactory == null)
- {
- throw new ArgumentNullException(nameof(mqttFactory));
- }
-
- return mqttFactory.UseClientAdapterFactory(new WebSocket4NetMqttClientAdapterFactory());
- }
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.WebSocket4Net/WebSocket4NetMqttChannel.cs b/Source/MQTTnet.Extensions.WebSocket4Net/WebSocket4NetMqttChannel.cs
deleted file mode 100644
index 1731e7a13..000000000
--- a/Source/MQTTnet.Extensions.WebSocket4Net/WebSocket4NetMqttChannel.cs
+++ /dev/null
@@ -1,256 +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.Concurrent;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Net;
-using System.Security.Authentication;
-using System.Security.Cryptography.X509Certificates;
-using System.Threading;
-using System.Threading.Tasks;
-using MQTTnet.Channel;
-using MQTTnet.Client;
-using MQTTnet.Exceptions;
-using MQTTnet.Internal;
-using SuperSocket.ClientEngine;
-using WebSocket4Net;
-using DataReceivedEventArgs = WebSocket4Net.DataReceivedEventArgs;
-
-namespace MQTTnet.Extensions.WebSocket4Net
-{
- public sealed class WebSocket4NetMqttChannel : IMqttChannel
- {
- readonly MqttClientOptions _clientOptions;
- readonly BlockingCollection _receiveBuffer = new BlockingCollection();
- readonly MqttClientWebSocketOptions _webSocketOptions;
-
- WebSocket _webSocket;
-
- public WebSocket4NetMqttChannel(MqttClientOptions clientOptions, MqttClientWebSocketOptions webSocketOptions)
- {
- _clientOptions = clientOptions ?? throw new ArgumentNullException(nameof(clientOptions));
- _webSocketOptions = webSocketOptions ?? throw new ArgumentNullException(nameof(webSocketOptions));
- }
-
- public X509Certificate2 ClientCertificate { get; }
-
- public string Endpoint => _webSocketOptions.Uri;
-
- public bool IsSecureConnection { get; private set; }
-
- public Task ConnectAsync(CancellationToken cancellationToken)
- {
- var uri = _webSocketOptions.Uri;
- if (!uri.StartsWith("ws://", StringComparison.OrdinalIgnoreCase) && !uri.StartsWith("wss://", StringComparison.OrdinalIgnoreCase))
- {
- if (_webSocketOptions.TlsOptions?.UseTls == true)
- {
- uri = "wss://" + uri;
- }
- else
- {
- uri = "ws://" + uri;
- }
- }
-
- IsSecureConnection = uri.StartsWith("wss://", StringComparison.OrdinalIgnoreCase);
-
-#if NET48 || NETCOREAPP3_0_OR_GREATER
- var sslProtocols = _webSocketOptions.TlsOptions?.SslProtocol ?? SslProtocols.Tls12 | SslProtocols.Tls13;
-#else
- var sslProtocols = _webSocketOptions.TlsOptions?.SslProtocol ?? SslProtocols.Tls12 | (SslProtocols)0x00003000 /*Tls13*/;
-#endif
-
- var subProtocol = _webSocketOptions.SubProtocols.FirstOrDefault() ?? string.Empty;
-
- var cookies = new List>();
- if (_webSocketOptions.CookieContainer != null)
- {
- throw new NotSupportedException("Cookies are not supported.");
- }
-
- List> customHeaders = null;
- if (_webSocketOptions.RequestHeaders != null)
- {
- customHeaders = _webSocketOptions.RequestHeaders.ToList();
- }
-
- EndPoint proxy = null;
- if (_webSocketOptions.ProxyOptions != null)
- {
- throw new NotSupportedException("Proxies are not supported.");
- }
-
- // The user agent can be empty always because it is just added to the custom headers as "User-Agent".
- var userAgent = string.Empty;
-
- var origin = string.Empty;
- var webSocketVersion = WebSocketVersion.None;
- var receiveBufferSize = 0;
-
- var certificates = _webSocketOptions.TlsOptions?.ClientCertificatesProvider?.GetCertificates();
-
- _webSocket = new WebSocket(uri, subProtocol, cookies, customHeaders, userAgent, origin, webSocketVersion, proxy, sslProtocols, receiveBufferSize)
- {
- NoDelay = true,
- Security =
- {
- AllowUnstrustedCertificate = _webSocketOptions.TlsOptions?.AllowUntrustedCertificates == true,
- AllowCertificateChainErrors = _webSocketOptions.TlsOptions?.IgnoreCertificateChainErrors == true,
- Certificates = certificates
- }
- };
-
- return ConnectInternalAsync(cancellationToken);
- }
-
- public Task DisconnectAsync(CancellationToken cancellationToken)
- {
- if (_webSocket != null && _webSocket.State == WebSocketState.Open)
- {
- _webSocket.Close();
- }
-
- return CompletedTask.Instance;
- }
-
- public void Dispose()
- {
- if (_webSocket == null)
- {
- return;
- }
-
- _webSocket.DataReceived -= OnDataReceived;
- _webSocket.Error -= OnError;
- _webSocket.Dispose();
- _webSocket = null;
- }
-
- public Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- var readBytes = 0;
- while (count > 0 && !cancellationToken.IsCancellationRequested)
- {
- if (!_receiveBuffer.TryTake(out var @byte))
- {
- if (readBytes == 0)
- {
- // Block until at least one byte was received.
- @byte = _receiveBuffer.Take(cancellationToken);
- }
- else
- {
- return Task.FromResult(readBytes);
- }
- }
-
- buffer[offset] = @byte;
- offset++;
- count--;
- readBytes++;
- }
-
- return Task.FromResult(readBytes);
- }
-
- public Task WriteAsync(ArraySegment buffer, bool isEndOfPacket, CancellationToken cancellationToken)
- {
- _webSocket.Send(buffer.Array, buffer.Offset, buffer.Count);
- return CompletedTask.Instance;
- }
-
- async Task ConnectInternalAsync(CancellationToken cancellationToken)
- {
- _webSocket.Error += OnError;
- _webSocket.DataReceived += OnDataReceived;
-
- var promise = new AsyncTaskCompletionSource();
-
- void ErrorHandler(object sender, ErrorEventArgs e)
- {
- promise.TrySetException(e.Exception);
- }
-
- void OpenedHandler(object sender, EventArgs e)
- {
- promise.TrySetResult(true);
- }
-
- void ClosedHandler(object sender, EventArgs e)
- {
- promise.TrySetException(new MqttCommunicationException("Connection closed."));
- }
-
- try
- {
- _webSocket.Opened += OpenedHandler;
- _webSocket.Closed += ClosedHandler;
- _webSocket.Error += ErrorHandler;
-
- using (var timeout = new CancellationTokenSource(_clientOptions.Timeout))
- using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, cancellationToken))
- {
- using (linkedCts.Token.Register(() => promise.TrySetCanceled()))
- {
- try
- {
- // OpenAsync does basically the same but without support for a cancellation token.
- // So we do it on our own!
- _webSocket.Open();
-
- await promise.Task.ConfigureAwait(false);
- }
- catch (Exception exception)
- {
- var timeoutReached = timeout.IsCancellationRequested && !cancellationToken.IsCancellationRequested;
- if (timeoutReached)
- {
- throw new MqttCommunicationTimedOutException(exception);
- }
-
- if (exception is AuthenticationException authenticationException)
- {
- throw new MqttCommunicationException(authenticationException.InnerException);
- }
-
- if (exception is OperationCanceledException)
- {
- throw new MqttCommunicationTimedOutException();
- }
-
- throw new MqttCommunicationException(exception);
- }
- }
- }
- }
- catch (OperationCanceledException)
- {
- throw new MqttCommunicationTimedOutException();
- }
- finally
- {
- _webSocket.Opened -= OpenedHandler;
- _webSocket.Closed -= ClosedHandler;
- _webSocket.Error -= ErrorHandler;
- }
- }
-
- void OnDataReceived(object sender, DataReceivedEventArgs e)
- {
- foreach (var @byte in e.Data)
- {
- _receiveBuffer.Add(@byte);
- }
- }
-
- static void OnError(object sender, ErrorEventArgs e)
- {
- Debug.Write(e.Exception.ToString());
- }
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.WebSocket4Net/WebSocket4NetMqttClientAdapterFactory.cs b/Source/MQTTnet.Extensions.WebSocket4Net/WebSocket4NetMqttClientAdapterFactory.cs
deleted file mode 100644
index 30138fe0e..000000000
--- a/Source/MQTTnet.Extensions.WebSocket4Net/WebSocket4NetMqttClientAdapterFactory.cs
+++ /dev/null
@@ -1,54 +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 MQTTnet.Adapter;
-using MQTTnet.Client;
-using MQTTnet.Diagnostics;
-using MQTTnet.Formatter;
-using MQTTnet.Implementations;
-
-namespace MQTTnet.Extensions.WebSocket4Net
-{
- public sealed class WebSocket4NetMqttClientAdapterFactory : IMqttClientAdapterFactory
- {
- public IMqttChannelAdapter CreateClientAdapter(MqttClientOptions options, MqttPacketInspector packetInspector, IMqttNetLogger logger)
- {
- if (options == null)
- {
- throw new ArgumentNullException(nameof(options));
- }
-
- switch (options.ChannelOptions)
- {
- case MqttClientTcpOptions _:
- {
- return new MqttChannelAdapter(
- new MqttTcpChannel(options),
- new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(options.WriterBufferSize, options.WriterBufferSizeMax)),
- logger)
- {
- PacketInspector = packetInspector
- };
- }
-
- case MqttClientWebSocketOptions webSocketOptions:
- {
- return new MqttChannelAdapter(
- new WebSocket4NetMqttChannel(options, webSocketOptions),
- new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(options.WriterBufferSize, options.WriterBufferSizeMax)),
- logger)
- {
- PacketInspector = packetInspector
- };
- }
-
- default:
- {
- throw new NotSupportedException();
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet/Server/Disconnecting/MqttServerClientDisconnectOptions.cs b/Source/MQTTnet.Server/Disconnecting/MqttServerClientDisconnectOptions.cs
similarity index 94%
rename from Source/MQTTnet/Server/Disconnecting/MqttServerClientDisconnectOptions.cs
rename to Source/MQTTnet.Server/Disconnecting/MqttServerClientDisconnectOptions.cs
index e9e61e9a6..763217fb1 100644
--- a/Source/MQTTnet/Server/Disconnecting/MqttServerClientDisconnectOptions.cs
+++ b/Source/MQTTnet.Server/Disconnecting/MqttServerClientDisconnectOptions.cs
@@ -2,11 +2,10 @@
// 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.Collections.Generic;
using MQTTnet.Packets;
using MQTTnet.Protocol;
-namespace MQTTnet.Server.Disconnecting
+namespace MQTTnet.Server
{
public sealed class MqttServerClientDisconnectOptions
{
diff --git a/Source/MQTTnet/Server/Disconnecting/MqttServerClientDisconnectOptionsBuilder.cs b/Source/MQTTnet.Server/Disconnecting/MqttServerClientDisconnectOptionsBuilder.cs
similarity index 95%
rename from Source/MQTTnet/Server/Disconnecting/MqttServerClientDisconnectOptionsBuilder.cs
rename to Source/MQTTnet.Server/Disconnecting/MqttServerClientDisconnectOptionsBuilder.cs
index 362585c65..7173ac64b 100644
--- a/Source/MQTTnet/Server/Disconnecting/MqttServerClientDisconnectOptionsBuilder.cs
+++ b/Source/MQTTnet.Server/Disconnecting/MqttServerClientDisconnectOptionsBuilder.cs
@@ -2,11 +2,10 @@
// 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.Collections.Generic;
using MQTTnet.Packets;
using MQTTnet.Protocol;
-namespace MQTTnet.Server.Disconnecting
+namespace MQTTnet.Server
{
public sealed class MqttServerClientDisconnectOptionsBuilder
{
diff --git a/Source/MQTTnet/Server/Events/ApplicationMessageEnqueuedEventArgs.cs b/Source/MQTTnet.Server/Events/ApplicationMessageEnqueuedEventArgs.cs
similarity index 100%
rename from Source/MQTTnet/Server/Events/ApplicationMessageEnqueuedEventArgs.cs
rename to Source/MQTTnet.Server/Events/ApplicationMessageEnqueuedEventArgs.cs
diff --git a/Source/MQTTnet/Server/Events/ApplicationMessageNotConsumedEventArgs.cs b/Source/MQTTnet.Server/Events/ApplicationMessageNotConsumedEventArgs.cs
similarity index 100%
rename from Source/MQTTnet/Server/Events/ApplicationMessageNotConsumedEventArgs.cs
rename to Source/MQTTnet.Server/Events/ApplicationMessageNotConsumedEventArgs.cs
diff --git a/Source/MQTTnet/Server/Events/ClientAcknowledgedPublishPacketEventArgs.cs b/Source/MQTTnet.Server/Events/ClientAcknowledgedPublishPacketEventArgs.cs
similarity index 100%
rename from Source/MQTTnet/Server/Events/ClientAcknowledgedPublishPacketEventArgs.cs
rename to Source/MQTTnet.Server/Events/ClientAcknowledgedPublishPacketEventArgs.cs
diff --git a/Source/MQTTnet/Server/Events/ClientConnectedEventArgs.cs b/Source/MQTTnet.Server/Events/ClientConnectedEventArgs.cs
similarity index 100%
rename from Source/MQTTnet/Server/Events/ClientConnectedEventArgs.cs
rename to Source/MQTTnet.Server/Events/ClientConnectedEventArgs.cs
diff --git a/Source/MQTTnet/Server/Events/ClientDisconnectedEventArgs.cs b/Source/MQTTnet.Server/Events/ClientDisconnectedEventArgs.cs
similarity index 100%
rename from Source/MQTTnet/Server/Events/ClientDisconnectedEventArgs.cs
rename to Source/MQTTnet.Server/Events/ClientDisconnectedEventArgs.cs
diff --git a/Source/MQTTnet/Server/Events/ClientSubscribedTopicEventArgs.cs b/Source/MQTTnet.Server/Events/ClientSubscribedTopicEventArgs.cs
similarity index 100%
rename from Source/MQTTnet/Server/Events/ClientSubscribedTopicEventArgs.cs
rename to Source/MQTTnet.Server/Events/ClientSubscribedTopicEventArgs.cs
diff --git a/Source/MQTTnet/Server/Events/ClientUnsubscribedTopicEventArgs.cs b/Source/MQTTnet.Server/Events/ClientUnsubscribedTopicEventArgs.cs
similarity index 100%
rename from Source/MQTTnet/Server/Events/ClientUnsubscribedTopicEventArgs.cs
rename to Source/MQTTnet.Server/Events/ClientUnsubscribedTopicEventArgs.cs
diff --git a/Source/MQTTnet/Server/Events/InterceptingClientApplicationMessageEnqueueEventArgs.cs b/Source/MQTTnet.Server/Events/InterceptingClientApplicationMessageEnqueueEventArgs.cs
similarity index 100%
rename from Source/MQTTnet/Server/Events/InterceptingClientApplicationMessageEnqueueEventArgs.cs
rename to Source/MQTTnet.Server/Events/InterceptingClientApplicationMessageEnqueueEventArgs.cs
diff --git a/Source/MQTTnet/Server/Events/InterceptingPacketEventArgs.cs b/Source/MQTTnet.Server/Events/InterceptingPacketEventArgs.cs
similarity index 100%
rename from Source/MQTTnet/Server/Events/InterceptingPacketEventArgs.cs
rename to Source/MQTTnet.Server/Events/InterceptingPacketEventArgs.cs
diff --git a/Source/MQTTnet/Server/Events/InterceptingPublishEventArgs.cs b/Source/MQTTnet.Server/Events/InterceptingPublishEventArgs.cs
similarity index 100%
rename from Source/MQTTnet/Server/Events/InterceptingPublishEventArgs.cs
rename to Source/MQTTnet.Server/Events/InterceptingPublishEventArgs.cs
diff --git a/Source/MQTTnet/Server/Events/InterceptingSubscriptionEventArgs.cs b/Source/MQTTnet.Server/Events/InterceptingSubscriptionEventArgs.cs
similarity index 100%
rename from Source/MQTTnet/Server/Events/InterceptingSubscriptionEventArgs.cs
rename to Source/MQTTnet.Server/Events/InterceptingSubscriptionEventArgs.cs
diff --git a/Source/MQTTnet/Server/Events/InterceptingUnsubscriptionEventArgs.cs b/Source/MQTTnet.Server/Events/InterceptingUnsubscriptionEventArgs.cs
similarity index 100%
rename from Source/MQTTnet/Server/Events/InterceptingUnsubscriptionEventArgs.cs
rename to Source/MQTTnet.Server/Events/InterceptingUnsubscriptionEventArgs.cs
diff --git a/Source/MQTTnet/Server/Events/LoadingRetainedMessagesEventArgs.cs b/Source/MQTTnet.Server/Events/LoadingRetainedMessagesEventArgs.cs
similarity index 100%
rename from Source/MQTTnet/Server/Events/LoadingRetainedMessagesEventArgs.cs
rename to Source/MQTTnet.Server/Events/LoadingRetainedMessagesEventArgs.cs
diff --git a/Source/MQTTnet/Server/Events/PreparingSessionEventArgs.cs b/Source/MQTTnet.Server/Events/PreparingSessionEventArgs.cs
similarity index 98%
rename from Source/MQTTnet/Server/Events/PreparingSessionEventArgs.cs
rename to Source/MQTTnet.Server/Events/PreparingSessionEventArgs.cs
index 584c4d4ec..d75ed503a 100644
--- a/Source/MQTTnet/Server/Events/PreparingSessionEventArgs.cs
+++ b/Source/MQTTnet.Server/Events/PreparingSessionEventArgs.cs
@@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using MQTTnet.Packets;
+using MQTTnet.Server.Internal;
namespace MQTTnet.Server
{
diff --git a/Source/MQTTnet/Server/Events/QueueMessageOverwrittenEventArgs.cs b/Source/MQTTnet.Server/Events/QueueMessageOverwrittenEventArgs.cs
similarity index 100%
rename from Source/MQTTnet/Server/Events/QueueMessageOverwrittenEventArgs.cs
rename to Source/MQTTnet.Server/Events/QueueMessageOverwrittenEventArgs.cs
diff --git a/Source/MQTTnet/Server/Events/RetainedMessageChangedEventArgs.cs b/Source/MQTTnet.Server/Events/RetainedMessageChangedEventArgs.cs
similarity index 100%
rename from Source/MQTTnet/Server/Events/RetainedMessageChangedEventArgs.cs
rename to Source/MQTTnet.Server/Events/RetainedMessageChangedEventArgs.cs
diff --git a/Source/MQTTnet/Server/Events/SessionDeletedEventArgs.cs b/Source/MQTTnet.Server/Events/SessionDeletedEventArgs.cs
similarity index 100%
rename from Source/MQTTnet/Server/Events/SessionDeletedEventArgs.cs
rename to Source/MQTTnet.Server/Events/SessionDeletedEventArgs.cs
diff --git a/Source/MQTTnet/Server/Events/ValidatingConnectionEventArgs.cs b/Source/MQTTnet.Server/Events/ValidatingConnectionEventArgs.cs
similarity index 98%
rename from Source/MQTTnet/Server/Events/ValidatingConnectionEventArgs.cs
rename to Source/MQTTnet.Server/Events/ValidatingConnectionEventArgs.cs
index 096b2da12..cf44bdf52 100644
--- a/Source/MQTTnet/Server/Events/ValidatingConnectionEventArgs.cs
+++ b/Source/MQTTnet.Server/Events/ValidatingConnectionEventArgs.cs
@@ -167,9 +167,6 @@ public ValidatingConnectionEventArgs(MqttConnectPacket connectPacket, IMqttChann
///
public ushort TopicAliasMaximum => _connectPacket.TopicAliasMaximum;
- [Obsolete("This property name has a typo. Use 'UserName' instead. This one will be removed soon.")]
- public string Username => _connectPacket.Username;
-
public string UserName => _connectPacket.Username;
///
diff --git a/Source/MQTTnet.Server/IMqttServerAdapter.cs b/Source/MQTTnet.Server/IMqttServerAdapter.cs
new file mode 100644
index 000000000..36cfb8718
--- /dev/null
+++ b/Source/MQTTnet.Server/IMqttServerAdapter.cs
@@ -0,0 +1,17 @@
+// 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.Adapter;
+using MQTTnet.Diagnostics.Logger;
+
+namespace MQTTnet.Server;
+
+public interface IMqttServerAdapter : IDisposable
+{
+ Func ClientHandler { get; set; }
+
+ Task StartAsync(MqttServerOptions options, IMqttNetLogger logger);
+
+ Task StopAsync();
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.Server/InjectedMqttApplicationMessage.cs b/Source/MQTTnet.Server/InjectedMqttApplicationMessage.cs
new file mode 100644
index 000000000..3a9add06a
--- /dev/null
+++ b/Source/MQTTnet.Server/InjectedMqttApplicationMessage.cs
@@ -0,0 +1,26 @@
+// 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.Collections;
+
+namespace MQTTnet.Server;
+
+public sealed class InjectedMqttApplicationMessage
+{
+ public InjectedMqttApplicationMessage(MqttApplicationMessage applicationMessage)
+ {
+ ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
+ }
+
+ public MqttApplicationMessage ApplicationMessage { get; }
+
+ ///