mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-15 00:15:15 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5691346a9 | ||
|
|
0730f21d29 | ||
|
|
6dfa7f0769 | ||
|
|
27592f1643 | ||
|
|
c6f4b59438 | ||
|
|
b362b2afc2 | ||
|
|
7a72a8d562 | ||
|
|
bd40e10a66 | ||
|
|
4b44a2b2b1 | ||
|
|
215f24a95b | ||
|
|
72f9acdc20 | ||
|
|
1e5390679f | ||
|
|
5cafce4345 | ||
|
|
dfaf3dc31f | ||
|
|
3a9397c793 | ||
|
|
1cf4a7d6be | ||
|
|
180588c3a4 | ||
|
|
bc4c86bebd | ||
|
|
ea88a8c0de | ||
|
|
d98129481a | ||
|
|
5e5818d7df | ||
|
|
72c7a8959c | ||
|
|
39e637795e | ||
|
|
731c89d2e9 | ||
|
|
25ddfc83a8 | ||
|
|
2d94abdde3 | ||
|
|
f0ea13c07f | ||
|
|
47cd5aa3cd | ||
|
|
258df6bb14 | ||
|
|
85c3c0b352 | ||
|
|
8020e66f41 | ||
|
|
bd004ec327 | ||
|
|
1e68c2e815 | ||
|
|
d7e1c89d62 | ||
|
|
b779d04636 | ||
|
|
623d8cafaa | ||
|
|
0920c86d37 | ||
|
|
c756a4092f | ||
|
|
feb53e78c6 | ||
|
|
dd0aba0640 | ||
|
|
68f1043ef5 | ||
|
|
d01d859227 | ||
|
|
fcab0d5cf0 | ||
|
|
b66a18f8dd | ||
|
|
8e1e93c00f | ||
|
|
cd8940f9b7 | ||
|
|
9d9077f17c | ||
|
|
fa6859d68a | ||
|
|
4f14373117 | ||
|
|
90ec39f2a4 | ||
|
|
2aa4d1b77a | ||
|
|
aa9f8d8ef3 | ||
|
|
50381637af | ||
|
|
8905b612c9 | ||
|
|
3cdf6d1261 | ||
|
|
d1c883ebae | ||
|
|
ed5e6ee897 | ||
|
|
2e019f372c | ||
|
|
cc9bc2d3a0 | ||
|
|
306bce5a58 | ||
|
|
6fe7ce7a0c | ||
|
|
8f1d4e3c22 | ||
|
|
77338527b4 | ||
|
|
962622a9f7 | ||
|
|
899ed7b1da | ||
|
|
10f7d809f2 | ||
|
|
324fb7e48c | ||
|
|
d913d4402e | ||
|
|
df262b5588 | ||
|
|
27daf37ba4 | ||
|
|
e5e10eee6d | ||
|
|
e174546b5a | ||
|
|
f99daee4d1 | ||
|
|
9b10eadb24 | ||
|
|
c39da42ef2 | ||
|
|
85b339e6f0 | ||
|
|
fbb5cd45a0 | ||
|
|
d430ca0250 | ||
|
|
cf8ea872d3 | ||
|
|
d4440f3be1 | ||
|
|
b97d01b78b | ||
|
|
375ee5c4ac | ||
|
|
90b37a1e2d | ||
|
|
0978f6cc75 | ||
|
|
e7c51e8dbc | ||
|
|
aab4448980 | ||
|
|
52139c6a99 | ||
|
|
f3d121120b | ||
|
|
413c68de44 | ||
|
|
f0919487f2 | ||
|
|
ffa7c51899 | ||
|
|
493cad087f | ||
|
|
4d94e1a318 | ||
|
|
28ff08ceb9 | ||
|
|
0f3d461c37 | ||
|
|
4285169108 | ||
|
|
1b1f2db1e6 | ||
|
|
d7248c0f64 | ||
|
|
d2e1b12186 | ||
|
|
dafcfb47d2 | ||
|
|
897f0f36d1 | ||
|
|
9cb12ac2ce | ||
|
|
312fb2bb3c | ||
|
|
3ee785c781 | ||
|
|
0ea4ae7501 | ||
|
|
7e764c7f0f | ||
|
|
5c2879029b | ||
|
|
6cbb8d42e3 | ||
|
|
d8d50f6dcb | ||
|
|
a01dc9fef8 | ||
|
|
3427d82efc | ||
|
|
5915285522 | ||
|
|
12f346115f | ||
|
|
05827341d4 | ||
|
|
1e88d6a05b | ||
|
|
8261ba9dc1 | ||
|
|
b766c1bebb | ||
|
|
6c188899fb | ||
|
|
23c5270c3f | ||
|
|
2d4ea8ee9b | ||
|
|
15d3761843 | ||
|
|
ed520455d0 | ||
|
|
cfc4ebe6a3 | ||
|
|
120b7fb7d1 | ||
|
|
8b164a4846 | ||
|
|
fda4272611 | ||
|
|
88cc79d52f | ||
|
|
8ba960343c | ||
|
|
0976fa682a | ||
|
|
18cc454842 | ||
|
|
e342a51d50 | ||
|
|
57652c76cf | ||
|
|
bb52159e49 | ||
|
|
109acccd19 | ||
|
|
deb2c3dec6 | ||
|
|
63ca6086d5 | ||
|
|
3f92f2ca2b | ||
|
|
a13874d8a5 | ||
|
|
c91a1f8572 | ||
|
|
b103718bd9 | ||
|
|
b88924bce5 | ||
|
|
f3f1d81b52 | ||
|
|
031b60878d | ||
|
|
1fbf20a569 | ||
|
|
b1bf7698aa | ||
|
|
fbf7d9bb5c | ||
|
|
822f582e41 | ||
|
|
f8eaf02516 | ||
|
|
df45540d6b | ||
|
|
2abcf6d8bc | ||
|
|
c1001db450 | ||
|
|
fcbe2e32f6 | ||
|
|
f955b4a3e6 | ||
|
|
3d8d06f9b3 | ||
|
|
d804b805c1 | ||
|
|
bba04e2b5d | ||
|
|
5eab337e13 | ||
|
|
e1d39a3fef | ||
|
|
3f5d627e9e | ||
|
|
2047f44554 | ||
|
|
75845d6416 | ||
|
|
74e952bb03 | ||
|
|
3773048bc5 | ||
|
|
f9aec0c067 | ||
|
|
91c8a15f65 | ||
|
|
b95d6b08bc | ||
|
|
a44ea74739 | ||
|
|
02e65bca67 | ||
|
|
37b783908a | ||
|
|
72211ae85a | ||
|
|
1f78a92c4d | ||
|
|
f92cfcd293 | ||
|
|
86256d1b10 | ||
|
|
c3e65390ab | ||
|
|
b0699003e2 | ||
|
|
1214b15260 | ||
|
|
322067466b | ||
|
|
ef2ed1c897 | ||
|
|
9fddfb3e77 | ||
|
|
ec015e7620 | ||
|
|
7738c5faa1 | ||
|
|
01bb09ff4b | ||
|
|
e1494c1004 | ||
|
|
1e2231c461 | ||
|
|
de0f5dc1f0 | ||
|
|
a93f44217b | ||
|
|
f9c3014e52 | ||
|
|
483c2d8b10 | ||
|
|
02ab0e4903 | ||
|
|
0bc25571e0 | ||
|
|
5b29b8cfd8 | ||
|
|
dd9dc054e8 | ||
|
|
b7c178b85c | ||
|
|
f8e29f8dc0 | ||
|
|
3d68951f68 | ||
|
|
7eb6d2d1e3 | ||
|
|
0446635ed6 | ||
|
|
4514348821 | ||
|
|
7b32abe625 | ||
|
|
0febc12b3d | ||
|
|
d0a848c8b4 | ||
|
|
fe69f5a633 | ||
|
|
ca95754fc3 | ||
|
|
cc405c3e90 | ||
|
|
315206f05e | ||
|
|
f252374437 | ||
|
|
38d3531db6 | ||
|
|
da5a172504 | ||
|
|
f894622884 | ||
|
|
88b0af9b21 | ||
|
|
e1bf9de347 | ||
|
|
6c15693456 | ||
|
|
4608930a37 | ||
|
|
eaf1195f95 | ||
|
|
a35dee9352 | ||
|
|
6cef746ed9 | ||
|
|
344cf674e6 | ||
|
|
26af205ab0 | ||
|
|
06c9d27c87 | ||
|
|
14224e4180 | ||
|
|
b954c9512e | ||
|
|
fb2be79673 | ||
|
|
44b7133a1c | ||
|
|
2f0513212e | ||
|
|
1a9460776b | ||
|
|
2716db04e3 | ||
|
|
3e4f4a59a8 | ||
|
|
24f0ea01f5 | ||
|
|
1625aadae9 | ||
|
|
e800901b4c | ||
|
|
34ae54d542 | ||
|
|
838d50c1d3 | ||
|
|
558d281a33 | ||
|
|
f8c956a9e2 | ||
|
|
9a6823597e | ||
|
|
42c1155bee | ||
|
|
f773257c86 | ||
|
|
4e9df98817 | ||
|
|
3208045baa | ||
|
|
7274f4390c | ||
|
|
11c8abfd54 | ||
|
|
f606a5996b | ||
|
|
58ad599783 | ||
|
|
ccd102a59f | ||
|
|
05101f0e0e | ||
|
|
f9630ef347 | ||
|
|
c5b7735a2c | ||
|
|
12fca2f05a | ||
|
|
b7fb0c1002 | ||
|
|
3ea281ff84 | ||
|
|
c778cd7a0f | ||
|
|
adce5662eb |
+81
-2
@@ -1,5 +1,84 @@
|
||||
[*.cs]
|
||||
[*.cs]
|
||||
|
||||
|
||||
# CA1031: Do not catch general exception types
|
||||
dotnet_diagnostic.CA1031.severity = none
|
||||
|
||||
# Microsoft .NET properties
|
||||
csharp_new_line_before_members_in_object_initializers = false
|
||||
csharp_preferred_modifier_order = public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
|
||||
csharp_style_var_elsewhere = true:suggestion
|
||||
csharp_style_var_for_built_in_types = true:suggestion
|
||||
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||
dotnet_naming_rule.constants_rule.severity = warning
|
||||
dotnet_naming_rule.constants_rule.style = upper_camel_case_style
|
||||
dotnet_naming_rule.constants_rule.symbols = constants_symbols
|
||||
dotnet_naming_rule.public_fields_rule.severity = warning
|
||||
dotnet_naming_rule.public_fields_rule.style = lower_camel_case_style
|
||||
dotnet_naming_rule.public_fields_rule.symbols = public_fields_symbols
|
||||
dotnet_naming_rule.static_readonly_rule.severity = warning
|
||||
dotnet_naming_rule.static_readonly_rule.style = upper_camel_case_style
|
||||
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_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_kinds = field
|
||||
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_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
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
||||
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||
dotnet_style_qualification_for_event = false:suggestion
|
||||
dotnet_style_qualification_for_field = false:suggestion
|
||||
dotnet_style_qualification_for_method = false:suggestion
|
||||
dotnet_style_qualification_for_property = false:suggestion
|
||||
|
||||
# ReSharper properties
|
||||
resharper_autodetect_indent_settings = true
|
||||
resharper_default_private_modifier = implicit
|
||||
resharper_place_accessorholder_attribute_on_same_line = false
|
||||
resharper_use_indent_from_vs = false
|
||||
|
||||
# ReSharper inspection severities
|
||||
resharper_arrange_redundant_parentheses_highlighting = hint
|
||||
resharper_arrange_this_qualifier_highlighting = hint
|
||||
resharper_built_in_type_reference_style_for_member_access_highlighting = hint
|
||||
resharper_built_in_type_reference_style_highlighting = hint
|
||||
resharper_redundant_base_qualifier_highlighting = warning
|
||||
resharper_suggest_var_or_type_built_in_types_highlighting = hint
|
||||
resharper_suggest_var_or_type_elsewhere_highlighting = hint
|
||||
resharper_suggest_var_or_type_simple_types_highlighting = hint
|
||||
resharper_web_config_module_not_resolved_highlighting = warning
|
||||
resharper_web_config_type_not_resolved_highlighting = warning
|
||||
resharper_web_config_wrong_module_highlighting = warning
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = crlf
|
||||
trim_trailing_whitespace = false
|
||||
insert_final_newline = false
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[{*.har,*.inputactions,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,jest.config}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[{*.yaml,*.yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[{tsconfig.app.json,tsconfig.e2e.json,tsconfig.json,tsconfig.lib.json,tsconfig.spec.json}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.{appxmanifest,asax,ascx,aspx,axaml,build,cg,cginc,compute,cs,cshtml,dtd,hlsl,hlsli,hlslinc,master,nuspec,paml,razor,resw,resx,shader,skin,usf,ush,vb,xaml,xamlx,xoml,xsd}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
name: CI
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
dotnet-version: ['6.0.x']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup .NET Core SDK ${{ matrix.dotnet-version }}
|
||||
uses: actions/setup-dotnet@v1.7.2
|
||||
with:
|
||||
dotnet-version: ${{ matrix.dotnet-version }}
|
||||
|
||||
- name: Build
|
||||
shell: pwsh
|
||||
run: |
|
||||
Set-Location -Path "Build"
|
||||
.\build.ps1 1.2.3 4.5.6-rc1
|
||||
|
||||
|
||||
# - name: Install dependencies
|
||||
# run: dotnet restore MQTTnet.sln
|
||||
# - name: Build
|
||||
# run: dotnet build --configuration Release --no-restore MQTTnet.sln
|
||||
# - name: Test MQTTnet
|
||||
# run: dotnet test --no-restore --verbosity normal Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj
|
||||
# - name: Test AspNetCore
|
||||
# run: dotnet test --no-restore --verbosity normal Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj
|
||||
@@ -294,3 +294,5 @@ __pycache__/
|
||||
/Tests/MQTTnet.TestApp.NetCore/RetainedMessages.json
|
||||
|
||||
Build/NuGet/
|
||||
|
||||
Build/nuget.exe
|
||||
|
||||
@@ -12,17 +12,21 @@
|
||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||
<description>This is a support library to integrate MQTTnet into AspNetCore.</description>
|
||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2020</copyright>
|
||||
<copyright>Copyright Christian Kratky 2016-2021</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
|
||||
<dependencies>
|
||||
<group targetFramework="netstandard2.0">
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
<dependency id="Microsoft.AspNetCore.Http.Connections" version="1.0.0" />
|
||||
</group>
|
||||
|
||||
|
||||
<group targetFramework="netcoreapp3.1">
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
</group>
|
||||
|
||||
<group targetFramework="net5.0">
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
</group>
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
@@ -33,10 +37,13 @@
|
||||
<!-- Images -->
|
||||
<file src="..\Images\Logo_128x128.png" target="images\" />
|
||||
|
||||
<!-- .NET 5.0 -->
|
||||
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\net5.0\MQTTnet.AspNetCore.*" target="lib\net5.0\" />
|
||||
|
||||
<!-- .NET Standard 2.0 -->
|
||||
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\netstandard2.0\MQTTnet.AspNetCore.*" target="lib\netstandard2.0\"/>
|
||||
|
||||
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\netstandard2.0\MQTTnet.AspNetCore.*" target="lib\netstandard2.0\" />
|
||||
|
||||
<!-- .NET Core App 3.1 -->
|
||||
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\netcoreapp3.1\MQTTnet.AspNetCore.*" target="lib\netcoreapp3.1\"/>
|
||||
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\netcoreapp3.1\MQTTnet.AspNetCore.*" target="lib\netcoreapp3.1\" />
|
||||
</files>
|
||||
</package>
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||
<description>This is an extension library which provides a managed MQTT client with additional features using MQTTnet.</description>
|
||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2020</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
|
||||
<copyright>Copyright Christian Kratky 2016-2021</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
</dependencies>
|
||||
@@ -26,20 +26,26 @@
|
||||
<!-- Images -->
|
||||
<file src="..\Images\Logo_128x128.png" target="images\" />
|
||||
|
||||
<!-- .NET 5.0 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net5.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net5.0\" />
|
||||
|
||||
<!-- .NET Standard 1.3 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard1.3\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard1.3\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard1.3\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard1.3\" />
|
||||
|
||||
<!-- .NET Standard 2.0 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard2.0\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard2.0\" />
|
||||
|
||||
<!-- .NET Standard 2.1 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.1\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard2.1\" />
|
||||
|
||||
<!-- Universal Windows -->
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\uap10.0\MQTTnet.Extensions.ManagedClient.*" target="lib\uap10.0\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\uap10.0\MQTTnet.Extensions.ManagedClient.*" target="lib\uap10.0\" />
|
||||
|
||||
<!-- .NET Framework -->
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net452\MQTTnet.Extensions.ManagedClient.*" target="lib\net452\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net452\MQTTnet.Extensions.ManagedClient.*" target="lib\net452\" />
|
||||
|
||||
<!-- .NET Framework 4.6.0 will use binaries from 4.5.2. -->
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net461\MQTTnet.Extensions.Rpc.*" target="lib\net461\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net461\MQTTnet.Extensions.ManagedClient.*" target="lib\net461\" />
|
||||
|
||||
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
|
||||
</files>
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||
<description>This is an extension library which allows executing synchronous device calls including a response using MQTTnet.</description>
|
||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2020</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
|
||||
<copyright>Copyright Christian Kratky 2016-2021</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
</dependencies>
|
||||
@@ -26,20 +26,26 @@
|
||||
<!-- Images -->
|
||||
<file src="..\Images\Logo_128x128.png" target="images\" />
|
||||
|
||||
<!-- .NET 5.0 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net5.0\MQTTnet.Extensions.Rpc.*" target="lib\net5.0\" />
|
||||
|
||||
<!-- .NET Standard 1.3 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard1.3\MQTTnet.Extensions.Rpc.*" target="lib\netstandard1.3\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard1.3\MQTTnet.Extensions.Rpc.*" target="lib\netstandard1.3\" />
|
||||
|
||||
<!-- .NET Standard 2.0 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\netstandard2.0\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\netstandard2.0\" />
|
||||
|
||||
<!-- .NET Standard 2.1 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.1\MQTTnet.Extensions.Rpc.*" target="lib\netstandard2.1\" />
|
||||
|
||||
<!-- Universal Windows -->
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\uap10.0\MQTTnet.Extensions.Rpc.*" target="lib\uap10.0\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\uap10.0\MQTTnet.Extensions.Rpc.*" target="lib\uap10.0\" />
|
||||
|
||||
<!-- .NET Framework -->
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net452\MQTTnet.Extensions.Rpc.*" target="lib\net452\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net452\MQTTnet.Extensions.Rpc.*" target="lib\net452\" />
|
||||
|
||||
<!-- .NET Framework 4.6.0 will use binaries from 4.5.2. -->
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net461\MQTTnet.Extensions.Rpc.*" target="lib\net461\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net461\MQTTnet.Extensions.Rpc.*" target="lib\net461\" />
|
||||
|
||||
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
|
||||
</files>
|
||||
|
||||
@@ -12,25 +12,11 @@
|
||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||
<description>This is an extension library which allows using _WebSocket4Net_ as transport for MQTTnet clients.</description>
|
||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2020</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
|
||||
<copyright>Copyright Christian Kratky 2016-2021</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
|
||||
<dependencies>
|
||||
<group targetFramework="netstandard1.3">
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
<dependency id="WebSocket4Net" version="0.15.2" />
|
||||
</group>
|
||||
<group targetFramework="netstandard2.0">
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
<dependency id="WebSocket4Net" version="0.15.2" />
|
||||
</group>
|
||||
<group targetFramework="netstandard2.1">
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
<dependency id="WebSocket4Net" version="0.15.2" />
|
||||
</group>
|
||||
<group targetFramework="uap10.0">
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
<dependency id="WebSocket4Net" version="0.15.2" />
|
||||
</group>
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
<dependency id="WebSocket4Net" version="0.15.2" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
@@ -41,20 +27,26 @@
|
||||
<!-- Images -->
|
||||
<file src="..\Images\Logo_128x128.png" target="images\" />
|
||||
|
||||
<!-- .NET 5.0 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\net5.0\MQTTnet.Extensions.WebSocket4Net.*" target="lib\net5.0\" />
|
||||
|
||||
<!-- .NET Standard 1.3 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\netstandard1.3\MQTTnet.Extensions.WebSocket4Net.*" target="lib\netstandard1.3\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\netstandard1.3\MQTTnet.Extensions.WebSocket4Net.*" target="lib\netstandard1.3\" />
|
||||
|
||||
<!-- .NET Standard 2.0 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\netstandard2.0\MQTTnet.Extensions.WebSocket4Net.*" target="lib\netstandard2.0\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\netstandard2.0\MQTTnet.Extensions.WebSocket4Net.*" target="lib\netstandard2.0\" />
|
||||
|
||||
<!-- .NET Standard 2.1 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\netstandard2.1\MQTTnet.Extensions.WebSocket4Net.*" target="lib\netstandard2.1\" />
|
||||
|
||||
<!-- Universal Windows -->
|
||||
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\uap10.0\MQTTnet.Extensions.WebSocket4Net.*" target="lib\uap10.0\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\uap10.0\MQTTnet.Extensions.WebSocket4Net.*" target="lib\uap10.0\" />
|
||||
|
||||
<!-- .NET Framework -->
|
||||
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\net452\MQTTnet.Extensions.WebSocket4Net.*" target="lib\net452\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\net452\MQTTnet.Extensions.WebSocket4Net.*" target="lib\net452\" />
|
||||
|
||||
<!-- .NET Framework 4.6.0 will use binaries from 4.5.2. -->
|
||||
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\net461\MQTTnet.Extensions.WebSocket4Net.*" target="lib\net461\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\net461\MQTTnet.Extensions.WebSocket4Net.*" target="lib\net461\" />
|
||||
|
||||
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
|
||||
</files>
|
||||
|
||||
+26
-36
@@ -12,41 +12,28 @@
|
||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports v3.1.0, v3.1.1 and v5.0.0 of the MQTT protocol.</description>
|
||||
<releaseNotes>
|
||||
* [Client] Fixed wrong value for "ClientWasConnected" in "MqttClientDisconnectedEventArgs" #976 (thanks to @dbeinder).
|
||||
* [Client] Added direct support for Amazon AWS connections (requires .NET Core 3.1) (thanks to @henning-krause).
|
||||
* [Client] handle disconnect package and propagate disconnect reason code (thanks to @JanEggers)
|
||||
* [ManagedClient] Exposed the internal MQTT client.
|
||||
* [Server] Added client message queue interceptor for QoS level (thanks to @msallin).
|
||||
* [Server] improved behavior when multiple connections fight over a session (thanks to @JanEggers)
|
||||
* [Server] Exposed _ClientDisconnectedInterceptor_ (thanks to @SeppPenner).
|
||||
* [Client] Increased delay for keep alive checks do decrease CPU load.
|
||||
* [Core] Decreased object allocations (#1324, thanks to @gfoidl).
|
||||
* [Core] Decreased object allocations when logging is not active (thanks to @gfoidl, @Tymoniden).
|
||||
* [Client] Fixed issue in _MqttApplicationMessageBuilder.WithPayload_ (#1322, thanks to @gfoidl).
|
||||
|
||||
Git commit: $gitCommit
|
||||
</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2020</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
|
||||
<copyright>Copyright Christian Kratky 2016-2021</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
|
||||
<dependencies>
|
||||
<group targetFramework="net452" />
|
||||
<group targetFramework="net461" />
|
||||
<group targetFramework="netstandard1.3">
|
||||
<dependency id="NETStandard.Library" version="1.3.0" />
|
||||
<dependency id="System.Net.Security" version="4.3.2" />
|
||||
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
||||
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
|
||||
</group>
|
||||
<group targetFramework="netstandard2.0">
|
||||
<dependency id="NETStandard.Library" version="2.0.0" />
|
||||
<dependency id="System.Net.Security" version="4.3.2" />
|
||||
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
||||
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
|
||||
</group>
|
||||
<group targetFramework="netstandard2.1">
|
||||
<dependency id="NETStandard.Library" version="2.0.0" />
|
||||
<dependency id="System.Net.Security" version="4.3.2" />
|
||||
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
||||
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
|
||||
</group>
|
||||
<group targetFramework="netcoreapp3.1">
|
||||
<dependency id="NETStandard.Library" version="2.0.0" />
|
||||
<dependency id="NETStandard.Library" version="1.6.1" />
|
||||
<dependency id="System.Net.Security" version="4.3.2" />
|
||||
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
||||
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
|
||||
</group>
|
||||
<group targetFramework="netstandard2.0" />
|
||||
<group targetFramework="netstandard2.1" />
|
||||
<group targetFramework="netcoreapp3.1" />
|
||||
<group targetFramework="net5.0" />
|
||||
<group targetFramework="uap10.0">
|
||||
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.2.10" />
|
||||
</group>
|
||||
@@ -60,26 +47,29 @@
|
||||
<!-- Images -->
|
||||
<file src="..\Images\Logo_128x128.png" target="images\" />
|
||||
|
||||
<!-- .NET 5.0 -->
|
||||
<file src="..\Source\MQTTnet\bin\Release\net5.0\MQTTnet.*" target="lib\net5.0\" />
|
||||
|
||||
<!-- .NET Standard 1.3 -->
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/>
|
||||
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\" />
|
||||
|
||||
<!-- .NET Standard 2.0 -->
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\netstandard2.0\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\netstandard2.0\" />
|
||||
|
||||
<!-- .NET Standard 2.1 -->
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.1\MQTTnet.*" target="lib\netstandard2.1\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.1\MQTTnet.*" target="lib\netstandard2.1\" />
|
||||
|
||||
<!-- .NET Standard 3.1 -->
|
||||
<file src="..\Source\MQTTnet\bin\Release\netcoreapp3.1\MQTTnet.*" target="lib\netcoreapp3.1\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\netcoreapp3.1\MQTTnet.*" target="lib\netcoreapp3.1\" />
|
||||
|
||||
<!-- Universal Windows -->
|
||||
<file src="..\Source\MQTTnet\bin\Release\uap10.0\MQTTnet.*" target="lib\uap10.0\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\uap10.0\MQTTnet.*" target="lib\uap10.0\" />
|
||||
|
||||
<!-- .NET Framework -->
|
||||
<file src="..\Source\MQTTnet\bin\Release\net452\MQTTnet.*" target="lib\net452\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\net452\MQTTnet.*" target="lib\net452\" />
|
||||
|
||||
<!-- .NET Framework 4.6.0 will use binaries from 4.5.2. -->
|
||||
<file src="..\Source\MQTTnet\bin\Release\net461\MQTTnet.*" target="lib\net461\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\net461\MQTTnet.*" target="lib\net461\" />
|
||||
|
||||
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
|
||||
</files>
|
||||
|
||||
+14
-49
@@ -5,6 +5,7 @@ if ([string]::IsNullOrEmpty($nugetVersion)) {$nugetVersion = "0.0.1"}
|
||||
|
||||
$vswhere = ${Env:\ProgramFiles(x86)} + '\Microsoft Visual Studio\Installer\vswhere'
|
||||
$msbuild = &$vswhere -products * -requires Microsoft.Component.MSBuild -latest -find MSBuild\**\Bin\MSBuild.exe
|
||||
$vstest = &$vswhere -products * -latest -find **\TestWindow\**\vstest.console.exe
|
||||
|
||||
Write-Host
|
||||
Write-Host "Assembly version = $assemblyVersion"
|
||||
@@ -20,8 +21,8 @@ Invoke-WebRequest -Uri "https://dist.nuget.org/win-x86-commandline/latest/nuget.
|
||||
&$msbuild ..\Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /verbosity:m
|
||||
&$msbuild ..\Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /verbosity:m
|
||||
|
||||
vstest.console.exe ..\Tests\MQTTnet.Core.Tests\bin\Release\netcoreapp3.1\MQTTnet.Tests.dll
|
||||
vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\MQTTnet.AspNetCore.Tests.dll
|
||||
&$vstest ..\Tests\MQTTnet.Core.Tests\bin\Release\netcoreapp3.1\MQTTnet.Tests.dll
|
||||
&$vstest ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\MQTTnet.AspNetCore.Tests.dll
|
||||
|
||||
# Build the core library
|
||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
@@ -31,10 +32,12 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M
|
||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
|
||||
# Build the ASP.NET Core 2.0 extension
|
||||
&$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
|
||||
# Build the RPC extension
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
@@ -43,6 +46,7 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
|
||||
# Build the Managed Client extension
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
@@ -51,6 +55,7 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
|
||||
# Build the WebSocket4Net extension
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
@@ -59,11 +64,16 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
|
||||
# Create NuGet packages.
|
||||
|
||||
Remove-Item .\NuGet -Force -Recurse -ErrorAction SilentlyContinue
|
||||
|
||||
$gitCommit = git log -1 --format=%h
|
||||
|
||||
Copy-Item MQTTnet.nuspec -Destination MQTTnet.nuspec.old -Force
|
||||
(Get-Content MQTTnet.nuspec) -replace '\$gitCommit', $gitCommit | Set-Content MQTTnet.nuspec
|
||||
Copy-Item MQTTnet.AspNetCore.nuspec -Destination MQTTnet.AspNetCore.nuspec.old -Force
|
||||
(Get-Content MQTTnet.AspNetCore.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.AspNetCore.nuspec
|
||||
Copy-Item MQTTnet.Extensions.Rpc.nuspec -Destination MQTTnet.Extensions.Rpc.nuspec.old -Force
|
||||
@@ -80,55 +90,10 @@ New-Item -ItemType Directory -Force -Path .\NuGet
|
||||
.\nuget.exe pack MQTTnet.Extensions.ManagedClient.nuspec -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.Extensions.WebSocket4Net.nuspec -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -OutputDir "NuGet" -Version $nugetVersion
|
||||
|
||||
Move-Item MQTTnet.nuspec.old -Destination MQTTnet.nuspec -Force
|
||||
Move-Item MQTTnet.AspNetCore.nuspec.old -Destination MQTTnet.AspNetCore.nuspec -Force
|
||||
Move-Item MQTTnet.Extensions.Rpc.nuspec.old -Destination MQTTnet.Extensions.Rpc.nuspec -Force
|
||||
Move-Item MQTTnet.Extensions.ManagedClient.nuspec.old -Destination MQTTnet.Extensions.ManagedClient.nuspec -Force
|
||||
Move-Item MQTTnet.Extensions.WebSocket4Net.nuspec.old -Destination MQTTnet.Extensions.WebSocket4Net.nuspec -Force
|
||||
|
||||
Remove-Item "nuget.exe" -Force -Recurse -ErrorAction SilentlyContinue
|
||||
|
||||
####################################################################
|
||||
|
||||
# Build MQTTnet.Server Portable
|
||||
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion
|
||||
|
||||
$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\publish"
|
||||
$destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Portable-v$nugetVersion.zip"
|
||||
If(Test-path $destination) {Remove-item $destination}
|
||||
Add-Type -assembly "system.io.compression.filesystem"
|
||||
[io.compression.zipfile]::CreateFromDirectory($source, $destination)
|
||||
|
||||
####################################################################
|
||||
|
||||
# Build MQTTnet.Server Linux-x64
|
||||
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime linux-x64
|
||||
|
||||
$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\linux-x64\publish"
|
||||
$destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Linux-x64-v$nugetVersion.zip"
|
||||
If(Test-path $destination) {Remove-item $destination}
|
||||
Add-Type -assembly "system.io.compression.filesystem"
|
||||
[io.compression.zipfile]::CreateFromDirectory($source, $destination)
|
||||
|
||||
####################################################################
|
||||
|
||||
# Build MQTTnet.Server Linux-ARM
|
||||
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime linux-arm
|
||||
|
||||
$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\linux-ARM\publish"
|
||||
$destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Linux-ARM-v$nugetVersion.zip"
|
||||
If(Test-path $destination) {Remove-item $destination}
|
||||
Add-Type -assembly "system.io.compression.filesystem"
|
||||
[io.compression.zipfile]::CreateFromDirectory($source, $destination)
|
||||
|
||||
####################################################################
|
||||
|
||||
# Build MQTTnet.Server Windows-x64
|
||||
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime win-x64
|
||||
|
||||
$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\win-x64\publish"
|
||||
$destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Windows-x64-v$nugetVersion.zip"
|
||||
If(Test-path $destination) {Remove-item $destination}
|
||||
Add-Type -assembly "system.io.compression.filesystem"
|
||||
[io.compression.zipfile]::CreateFromDirectory($source, $destination)
|
||||
|
||||
####################################################################
|
||||
Remove-Item "nuget.exe" -Force -Recurse -ErrorAction SilentlyContinue
|
||||
@@ -0,0 +1,6 @@
|
||||
# Code of Conduct
|
||||
|
||||
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).
|
||||
@@ -45,8 +45,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore.Tests",
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj", "{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Server", "Source\MQTTnet.Server\MQTTnet.Server.csproj", "{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.WebSocket4Net", "Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj", "{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}"
|
||||
EndProject
|
||||
Global
|
||||
|
||||
+3
-50
@@ -9,8 +9,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{32A630A7-2598-41D7-B625-204CD906F5FB}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.TestApp.UniversalWindows", "Tests\MQTTnet.TestApp.UniversalWindows\MQTTnet.TestApp.UniversalWindows.csproj", "{FF1F72D6-9524-4422-9497-3CC0002216ED}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "Source\MQTTnet\MQTTnet.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{67C28AC1-BC3A-420A-BE9C-FA2401431CF9}"
|
||||
@@ -49,19 +47,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.ManagedC
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore.Tests", "Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Server", "Source\MQTTnet.Server\MQTTnet.Server.csproj", "{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}"
|
||||
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("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Apps", "Apps", "{A56E3128-1639-4F31-873A-325E14BB6295}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Blazor", "Blazor", "{61B165A0-5AA8-4E04-A53D-A22A84AA6EB7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Test.BlazorApp.Server", "Tests\MQTTnet.Test.BlazorApp\Server\MQTTnet.Test.BlazorApp.Server.csproj", "{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Test.BlazorApp.Server", "Tests\MQTTnet.Test.BlazorApp\Server\MQTTnet.Test.BlazorApp.Server.csproj", "{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Test.BlazorApp.Client", "Tests\MQTTnet.Test.BlazorApp\Client\MQTTnet.Test.BlazorApp.Client.csproj", "{D260D63D-7902-4C55-9665-84C5CACBBB24}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Test.BlazorApp.Client", "Tests\MQTTnet.Test.BlazorApp\Client\MQTTnet.Test.BlazorApp.Client.csproj", "{D260D63D-7902-4C55-9665-84C5CACBBB24}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Test.BlazorApp.Shared", "Tests\MQTTnet.Test.BlazorApp\Shared\MQTTnet.Test.BlazorApp.Shared.csproj", "{DDB069BA-6E1A-48C7-B374-5D903B553CAD}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Test.BlazorApp.Shared", "Tests\MQTTnet.Test.BlazorApp\Shared\MQTTnet.Test.BlazorApp.Shared.csproj", "{DDB069BA-6E1A-48C7-B374-5D903B553CAD}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -91,29 +85,6 @@ Global
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.Build.0 = Release|Any CPU
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.Deploy.0 = Debug|ARM
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x64.Build.0 = Debug|x64
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x64.Deploy.0 = Debug|x64
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x86.Build.0 = Debug|x86
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x86.Deploy.0 = Debug|x86
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|ARM.Build.0 = Release|ARM
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|ARM.Deploy.0 = Release|ARM
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x64.ActiveCfg = Release|x64
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x64.Build.0 = Release|x64
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x64.Deploy.0 = Release|x64
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x86.ActiveCfg = Release|x86
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x86.Build.0 = Release|x86
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x86.Deploy.0 = Release|x86
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
@@ -242,22 +213,6 @@ Global
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|x86.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}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
@@ -328,7 +283,6 @@ Global
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED} = {A56E3128-1639-4F31-873A-325E14BB6295}
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
@@ -337,7 +291,6 @@ Global
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B} = {A56E3128-1639-4F31-873A-325E14BB6295}
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
|
||||
{61B165A0-5AA8-4E04-A53D-A22A84AA6EB7} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F} = {61B165A0-5AA8-4E04-A53D-A22A84AA6EB7}
|
||||
|
||||
@@ -11,4 +11,6 @@
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=PINGREQ/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=unsub/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
@@ -5,10 +5,9 @@
|
||||
</p>
|
||||
|
||||
[](https://www.nuget.org/packages/MQTTnet)
|
||||
[](https://ci.appveyor.com/project/chkr1011/mqttnet)
|
||||
[](https://bettercodehub.com/)
|
||||
[](https://gitter.im/MQTTnet/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://raw.githubusercontent.com/chkr1011/MQTTnet/master/LICENSE)
|
||||
[](https://raw.githubusercontent.com/chkr1011/MQTTnet/master/LICENSE)
|
||||
|
||||
# MQTTnet
|
||||
|
||||
@@ -19,7 +18,7 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
|
||||
### General
|
||||
|
||||
* Async support
|
||||
* TLS 1.2 support for client and server (but not UWP servers)
|
||||
* TLS support for client and server (but not UWP servers)
|
||||
* Extensible communication channels (e.g. In-Memory, TCP, TCP+TLS, WS)
|
||||
* Lightweight (only the low level implementation of MQTT, no overhead)
|
||||
* Performance optimized (processing ~70.000 messages / second)*
|
||||
@@ -56,7 +55,7 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
|
||||
|
||||
_MQTTnet Server_ is a standalone cross platform MQTT server (like mosquitto) basing on this library. It has the following features.
|
||||
* Running portable (no installation required)
|
||||
* Runs und Windows, Linux, macOS, Raspberry Pi
|
||||
* Runs under Windows, Linux, macOS, Raspberry Pi
|
||||
* Python scripting support for manipulating messages, validation of clients, building business logic etc.
|
||||
* Supports WebSocket and TCP (with and without TLS) connections
|
||||
* Provides a HTTP based API (including Swagger endpoint)
|
||||
@@ -64,15 +63,32 @@ _MQTTnet Server_ is a standalone cross platform MQTT server (like mosquitto) bas
|
||||
|
||||
## Supported frameworks
|
||||
|
||||
* .NET 5.0+
|
||||
* .NET Standard 1.3+
|
||||
* .NET Core 1.1+
|
||||
* .NET Core App 1.1+
|
||||
* .NET Framework 4.5.2+ (x86, x64, AnyCPU)
|
||||
* .NET Framework 4.5.2+
|
||||
* Mono 5.2+
|
||||
* Universal Windows Platform (UWP) 10.0.10240+ (x86, x64, ARM, AnyCPU, Windows 10 IoT Core)
|
||||
* Universal Windows Platform (UWP) 10.0.10240+ (Windows 10 IoT Core)
|
||||
* Xamarin.Android 7.5+
|
||||
* Xamarin.iOS 10.14+
|
||||
* Blazor WebAssembly 3.2.0
|
||||
* Blazor WebAssembly 3.2.0+
|
||||
|
||||
## Supported platforms
|
||||
|
||||
* x86
|
||||
* x64
|
||||
* AnyCPU
|
||||
* ARM
|
||||
|
||||
## Supported OS
|
||||
|
||||
* Windows
|
||||
* Windows 10 IoT Core
|
||||
* Linux (Ubuntu, Raspbian etc.)
|
||||
* macOS
|
||||
* Android
|
||||
* iOS
|
||||
|
||||
## Supported MQTT versions
|
||||
|
||||
@@ -97,11 +113,15 @@ Also additions and updates in the Wiki are welcome.
|
||||
|
||||
This library is used in the following projects:
|
||||
|
||||
* Azure Functions MQTT Bindings, [CaseOnline.Azure.WebJobs.Extensions.Mqtt](https://github.com/keesschollaart81/CaseOnline.Azure.WebJobs.Extensions.Mqtt/)
|
||||
* Azure Functions MQTT Bindings, <https://github.com/keesschollaart81/CaseOnline.Azure.WebJobs.Extensions.Mqtt>)
|
||||
* HA4IoT (Open Source Home Automation system for .NET, <https://github.com/chkr1011/HA4IoT>)
|
||||
* MQTT Client Rx (Wrapper for Reactive Extensions, <https://github.com/1iveowl/MQTTClient.rx>)
|
||||
* MQTT Client Rx (Managed Client Wrapper for Reactive Extensions, <https://github.com/mmuecke/RxMQTTnet>)
|
||||
* MQTT Tester (MQTT client test app for [Android](https://play.google.com/store/apps/details?id=com.liveowl.mqtttester) and [iOS](https://itunes.apple.com/us/app/mqtt-tester/id1278621826?mt=8))
|
||||
* MQTTnet App (Cross platform client application for MQTT debugging, inspection etc., <https://github.com/chkr1011/MQTTnet.App>)
|
||||
* Wirehome.Core (Open Source Home Automation system for .NET Core, <https://github.com/chkr1011/Wirehome.Core>)
|
||||
* SparkplugNet (Sparkplug library for .Net, <https://github.com/SeppPenner/SparkplugNet>)
|
||||
* Silverback (Framework to build event-driven applications - support for MQTT, Kafka & RabbitMQ) <https://github.com/BEagle1984/silverback>
|
||||
|
||||
Further projects using this project can be found under https://github.com/chkr1011/MQTTnet/network/dependents.
|
||||
|
||||
@@ -111,7 +131,7 @@ If you use this library and want to see your project here please create a pull r
|
||||
|
||||
MIT License
|
||||
|
||||
MQTTnet Copyright (c) 2016-2020 Christian Kratky
|
||||
MQTTnet Copyright (c) 2016-2021 Christian Kratky
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace MQTTnet.AspNetCore.Client.Tcp
|
||||
_sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
|
||||
_receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
|
||||
}
|
||||
#if NETCOREAPP3_1
|
||||
#if NETCOREAPP3_1 || NET5_0
|
||||
public override ValueTask DisposeAsync()
|
||||
#else
|
||||
public Task DisposeAsync()
|
||||
@@ -53,7 +53,7 @@ namespace MQTTnet.AspNetCore.Client.Tcp
|
||||
|
||||
_socket?.Dispose();
|
||||
|
||||
#if NETCOREAPP3_1
|
||||
#if NETCOREAPP3_1 || NET5_0
|
||||
|
||||
return base.DisposeAsync();
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace MQTTnet.AspNetCore.Extensions
|
||||
#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<MqttConnectionHandler>(path, options =>
|
||||
@@ -19,5 +20,6 @@ namespace MQTTnet.AspNetCore.Extensions
|
||||
options.WebSockets.SubProtocolSelector = MqttSubProtocolSelector.SelectSubProtocol;
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
#if NETCOREAPP3_1
|
||||
#if NETCOREAPP3_1 || NET5_0
|
||||
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Server;
|
||||
|
||||
@@ -61,7 +62,7 @@ namespace MQTTnet.AspNetCore.Extensions
|
||||
|
||||
private static IServiceCollection AddHostedMqttServer(this IServiceCollection services)
|
||||
{
|
||||
var logger = new MqttNetLogger();
|
||||
var logger = new MqttNetEventLogger();
|
||||
|
||||
services.AddSingleton<IMqttNetLogger>(logger);
|
||||
services.AddSingleton<MqttHostedServer>();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;netcoreapp2.1;netcoreapp3.1</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard2.0;netcoreapp2.1;netcoreapp3.1;net5.0</TargetFrameworks>
|
||||
<Product />
|
||||
<Company />
|
||||
<Authors />
|
||||
@@ -21,11 +21,11 @@
|
||||
<DefineConstants>RELEASE;NETSTANDARD2_0</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'net5.0' ">
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp3.1'">
|
||||
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp3.1' and '$(TargetFramework)' != 'net5.0' ">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Connections" Version="1.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -66,10 +66,10 @@ namespace MQTTnet.AspNetCore
|
||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||
|
||||
public long BytesSent { get; set; }
|
||||
|
||||
public long BytesReceived { get; set; }
|
||||
|
||||
public Action ReadingPacketStartedCallback { get; set; }
|
||||
public Action ReadingPacketCompletedCallback { get; set; }
|
||||
public bool IsReadingPacket { get; private set; }
|
||||
|
||||
IHttpContextFeature Http => Connection.Features.Get<IHttpContextFeature>();
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace MQTTnet.AspNetCore
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||
public async Task<MqttBasePacket> ReceivePacketAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var input = Connection.Transport.Input;
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace MQTTnet.AspNetCore
|
||||
else
|
||||
{
|
||||
// we did receive something but the message is not yet complete
|
||||
ReadingPacketStartedCallback?.Invoke();
|
||||
IsReadingPacket = true;
|
||||
}
|
||||
}
|
||||
else if (readResult.IsCompleted)
|
||||
@@ -147,14 +147,14 @@ namespace MQTTnet.AspNetCore
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// completing the cannels makes sure that there is no more data read after a protocol error
|
||||
// completing the channel makes sure that there is no more data read after a protocol error
|
||||
_input?.Complete(e);
|
||||
_output?.Complete(e);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ReadingPacketCompletedCallback?.Invoke();
|
||||
IsReadingPacket = false;
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
@@ -167,7 +167,7 @@ namespace MQTTnet.AspNetCore
|
||||
BytesSent = 0;
|
||||
}
|
||||
|
||||
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken)
|
||||
public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
|
||||
{
|
||||
var formatter = PacketFormatterAdapter;
|
||||
using (await _writerLock.WaitAsync(cancellationToken).ConfigureAwait(false))
|
||||
@@ -180,6 +180,7 @@ namespace MQTTnet.AspNetCore
|
||||
{
|
||||
BytesSent += msg.Length;
|
||||
}
|
||||
|
||||
PacketFormatterAdapter.FreeBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@ 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
|
||||
{
|
||||
public class MqttHostedServer : MqttServer, IHostedService
|
||||
public sealed class MqttHostedServer : MqttServer, IHostedService
|
||||
{
|
||||
private readonly IMqttServerOptions _options;
|
||||
readonly IMqttServerOptions _options;
|
||||
|
||||
public MqttHostedServer(IMqttServerOptions options, IEnumerable<IMqttServerAdapter> adapters, IMqttNetLogger logger)
|
||||
: base(adapters, logger)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Server;
|
||||
using System;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
@@ -49,7 +49,7 @@ namespace MQTTnet.AspNetCore
|
||||
var formatter = new MqttPacketFormatterAdapter(writer);
|
||||
var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection, clientCertificate);
|
||||
|
||||
using (var channelAdapter = new MqttChannelAdapter(channel, formatter, _rootLogger))
|
||||
using (var channelAdapter = new MqttChannelAdapter(channel, formatter, null, _rootLogger))
|
||||
{
|
||||
await clientHandler(channelAdapter).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Server;
|
||||
|
||||
@@ -8,8 +8,6 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
IMqttClientOptions ClientOptions { get; }
|
||||
|
||||
bool AutoReconnect { get; }
|
||||
|
||||
TimeSpan AutoReconnectDelay { get; }
|
||||
|
||||
TimeSpan ConnectionCheckInterval { get; }
|
||||
@@ -19,5 +17,11 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
int MaxPendingMessages { get; }
|
||||
|
||||
MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines the maximum amount of topic filters which will be sent in a SUBSCRIBE/UNSUBSCRIBE packet.
|
||||
/// Amazon AWS limits this number to 8. The default is int.MaxValue.
|
||||
/// </summary>
|
||||
int MaxTopicFiltersInSubscribeUnsubscribePackets { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;net5.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' AND '$(SolutionName)' != 'MQTTnet.noUWP' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
<Product />
|
||||
<Company />
|
||||
<Authors />
|
||||
<PackageId />
|
||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
<DelaySign>false</DelaySign>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Disconnecting;
|
||||
using MQTTnet.Client.Publishing;
|
||||
@@ -13,6 +13,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
@@ -28,12 +29,12 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
/// at reconnect and are solely owned by <see cref="MaintainConnectionAsync"/>.
|
||||
/// </summary>
|
||||
readonly Dictionary<string, MqttQualityOfServiceLevel> _reconnectSubscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
|
||||
|
||||
readonly Dictionary<string, MqttQualityOfServiceLevel> _subscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
|
||||
readonly HashSet<string> _unsubscriptions = new HashSet<string>();
|
||||
readonly SemaphoreSlim _subscriptionsQueuedSignal = new SemaphoreSlim(0);
|
||||
|
||||
readonly IMqttClient _mqttClient;
|
||||
readonly IMqttNetScopedLogger _logger;
|
||||
readonly MqttNetSourceLogger _logger;
|
||||
|
||||
readonly AsyncLock _messageQueueLock = new AsyncLock();
|
||||
|
||||
@@ -45,39 +46,38 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
public ManagedMqttClient(IMqttClient mqttClient, IMqttNetLogger logger)
|
||||
{
|
||||
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
|
||||
InternalClient = mqttClient;
|
||||
InternalClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
|
||||
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
_logger = logger.CreateScopedLogger(nameof(ManagedMqttClient));
|
||||
_logger = logger.WithSource(nameof(ManagedMqttClient));
|
||||
}
|
||||
|
||||
public bool IsConnected => _mqttClient.IsConnected;
|
||||
public bool IsConnected => InternalClient.IsConnected;
|
||||
|
||||
public bool IsStarted => _connectionCancellationToken != null;
|
||||
|
||||
|
||||
public IMqttClient InternalClient { get; }
|
||||
|
||||
public int PendingApplicationMessagesCount => _messageQueue.Count;
|
||||
|
||||
|
||||
public IManagedMqttClientOptions Options { get; private set; }
|
||||
|
||||
public IMqttClientConnectedHandler ConnectedHandler
|
||||
{
|
||||
get => _mqttClient.ConnectedHandler;
|
||||
set => _mqttClient.ConnectedHandler = value;
|
||||
get => InternalClient.ConnectedHandler;
|
||||
set => InternalClient.ConnectedHandler = value;
|
||||
}
|
||||
|
||||
public IMqttClientDisconnectedHandler DisconnectedHandler
|
||||
{
|
||||
get => _mqttClient.DisconnectedHandler;
|
||||
set => _mqttClient.DisconnectedHandler = value;
|
||||
get => InternalClient.DisconnectedHandler;
|
||||
set => InternalClient.DisconnectedHandler = value;
|
||||
}
|
||||
|
||||
public IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler
|
||||
{
|
||||
get => _mqttClient.ApplicationMessageReceivedHandler;
|
||||
set => _mqttClient.ApplicationMessageReceivedHandler = value;
|
||||
get => InternalClient.ApplicationMessageReceivedHandler;
|
||||
set => InternalClient.ApplicationMessageReceivedHandler = value;
|
||||
}
|
||||
|
||||
public IApplicationMessageProcessedHandler ApplicationMessageProcessedHandler { get; set; }
|
||||
@@ -94,14 +94,14 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
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)
|
||||
if (options.Storage != null)
|
||||
{
|
||||
_storageManager = new ManagedMqttClientStorageManager(Options.Storage);
|
||||
_storageManager = new ManagedMqttClientStorageManager(options.Storage);
|
||||
var messages = await _storageManager.LoadQueuedMessagesAsync().ConfigureAwait(false);
|
||||
|
||||
foreach (var message in messages)
|
||||
@@ -110,16 +110,13 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
}
|
||||
}
|
||||
|
||||
if (Options.AutoReconnect)
|
||||
{
|
||||
var cancellationTokenSource = new CancellationTokenSource();
|
||||
var cancellationToken = cancellationTokenSource.Token;
|
||||
_connectionCancellationToken = cancellationTokenSource;
|
||||
var cancellationTokenSource = new CancellationTokenSource();
|
||||
var cancellationToken = cancellationTokenSource.Token;
|
||||
_connectionCancellationToken = cancellationTokenSource;
|
||||
|
||||
_maintainConnectionTask = Task.Run(() => MaintainConnectionAsync(cancellationToken), cancellationToken);
|
||||
_maintainConnectionTask.RunInBackground(_logger);
|
||||
|
||||
_maintainConnectionTask = Task.Run(() => MaintainConnectionAsync(cancellationToken), cancellationToken);
|
||||
_maintainConnectionTask.Forget(_logger);
|
||||
}
|
||||
|
||||
_logger.Info("Started");
|
||||
}
|
||||
|
||||
@@ -141,7 +138,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
public Task PingAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return _mqttClient.PingAsync(cancellationToken);
|
||||
return InternalClient.PingAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken)
|
||||
@@ -210,7 +207,6 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
await applicationMessageSkippedHandler.HandleApplicationMessageSkippedAsync(applicationMessageSkippedEventArgs).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,6 +216,11 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
||||
|
||||
foreach (var topicFilter in topicFilters)
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalidSubscribe(topicFilter.Topic);
|
||||
}
|
||||
|
||||
lock (_subscriptions)
|
||||
{
|
||||
foreach (var topicFilter in topicFilters)
|
||||
@@ -228,6 +229,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
_unsubscriptions.Remove(topicFilter.Topic);
|
||||
}
|
||||
}
|
||||
|
||||
_subscriptionsQueuedSignal.Release();
|
||||
|
||||
return Task.FromResult(0);
|
||||
@@ -247,6 +249,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
_unsubscriptions.Add(topic);
|
||||
}
|
||||
}
|
||||
|
||||
_subscriptionsQueuedSignal.Release();
|
||||
|
||||
return Task.FromResult(0);
|
||||
@@ -267,7 +270,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
_messageQueue.Dispose();
|
||||
_messageQueueLock.Dispose();
|
||||
_mqttClient.Dispose();
|
||||
InternalClient.Dispose();
|
||||
_subscriptionsQueuedSignal.Dispose();
|
||||
}
|
||||
|
||||
@@ -296,7 +299,14 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
try
|
||||
{
|
||||
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
|
||||
using (var disconnectTimeout = new CancellationTokenSource(Options.ClientOptions.CommunicationTimeout))
|
||||
{
|
||||
await InternalClient.DisconnectAsync(disconnectTimeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger.Warning("Timeout while sending DISCONNECT packet.");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -305,7 +315,9 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
_logger.Info("Stopped");
|
||||
}
|
||||
|
||||
_reconnectSubscriptions.Clear();
|
||||
|
||||
lock (_subscriptions)
|
||||
{
|
||||
_subscriptions.Clear();
|
||||
@@ -318,7 +330,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
try
|
||||
{
|
||||
var connectionState = await ReconnectIfRequiredAsync().ConfigureAwait(false);
|
||||
var connectionState = await ReconnectIfRequiredAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (connectionState == ReconnectionResult.NotConnected)
|
||||
{
|
||||
StopPublishing();
|
||||
@@ -361,7 +373,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested && _mqttClient.IsConnected)
|
||||
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.
|
||||
@@ -400,7 +412,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
Exception transmitException = null;
|
||||
try
|
||||
{
|
||||
await _mqttClient.PublishAsync(message.ApplicationMessage).ConfigureAwait(false);
|
||||
await InternalClient.PublishAsync(message.ApplicationMessage).ConfigureAwait(false);
|
||||
|
||||
using (await _messageQueueLock.WaitAsync(CancellationToken.None).ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
|
||||
{
|
||||
@@ -422,7 +434,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
transmitException = exception;
|
||||
|
||||
_logger.Warning(exception, $"Publishing application ({message.Id}) message failed.");
|
||||
_logger.Warning(exception, "Publishing application message ({0}) failed.", message.Id);
|
||||
|
||||
if (message.ApplicationMessage.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
||||
{
|
||||
@@ -447,7 +459,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
catch (Exception exception)
|
||||
{
|
||||
transmitException = exception;
|
||||
_logger.Error(exception, $"Error while publishing application message ({message.Id}).");
|
||||
_logger.Error(exception, "Error while publishing application message ({0}).", message.Id);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -463,6 +475,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
async Task PublishSubscriptionsAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
var endTime = DateTime.UtcNow + timeout;
|
||||
|
||||
while (await _subscriptionsQueuedSignal.WaitAsync(GetRemainingTime(endTime), cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
List<MqttTopicFilter> subscriptions;
|
||||
@@ -470,8 +483,14 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
lock (_subscriptions)
|
||||
{
|
||||
subscriptions = _subscriptions.Select(i => new MqttTopicFilter { Topic = i.Key, QualityOfServiceLevel = i.Value }).ToList();
|
||||
subscriptions = _subscriptions.Select(i => new MqttTopicFilter
|
||||
{
|
||||
Topic = i.Key,
|
||||
QualityOfServiceLevel = i.Value
|
||||
}).ToList();
|
||||
|
||||
_subscriptions.Clear();
|
||||
|
||||
unsubscriptions = new HashSet<string>(_unsubscriptions);
|
||||
_unsubscriptions.Clear();
|
||||
}
|
||||
@@ -481,7 +500,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
continue;
|
||||
}
|
||||
|
||||
_logger.Verbose($"Publishing subscriptions ({subscriptions.Count} subscriptions and {unsubscriptions.Count} unsubscriptions)");
|
||||
_logger.Verbose("Publishing {0} added and {1} removed subscriptions", subscriptions.Count, unsubscriptions.Count);
|
||||
|
||||
foreach (var unsubscription in unsubscriptions)
|
||||
{
|
||||
@@ -493,22 +512,53 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
_reconnectSubscriptions[subscription.Topic] = subscription.QualityOfServiceLevel;
|
||||
}
|
||||
|
||||
try
|
||||
var addedTopicFilters = new List<MqttTopicFilter>();
|
||||
foreach (var subscription in subscriptions)
|
||||
{
|
||||
if (unsubscriptions.Any())
|
||||
addedTopicFilters.Add(subscription);
|
||||
|
||||
if (addedTopicFilters.Count == Options.MaxTopicFiltersInSubscribeUnsubscribePackets)
|
||||
{
|
||||
await _mqttClient.UnsubscribeAsync(unsubscriptions.ToArray()).ConfigureAwait(false);
|
||||
await SendSubscribeUnsubscribe(addedTopicFilters, null).ConfigureAwait(false);
|
||||
addedTopicFilters.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (subscriptions.Any())
|
||||
await SendSubscribeUnsubscribe(addedTopicFilters, null).ConfigureAwait(false);
|
||||
|
||||
var removedTopicFilters = new List<string>();
|
||||
foreach (var unSub in unsubscriptions)
|
||||
{
|
||||
removedTopicFilters.Add(unSub);
|
||||
|
||||
if (removedTopicFilters.Count == Options.MaxTopicFiltersInSubscribeUnsubscribePackets)
|
||||
{
|
||||
await _mqttClient.SubscribeAsync(subscriptions.ToArray()).ConfigureAwait(false);
|
||||
await SendSubscribeUnsubscribe(null, removedTopicFilters).ConfigureAwait(false);
|
||||
removedTopicFilters.Clear();
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
|
||||
await SendSubscribeUnsubscribe(null, removedTopicFilters).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
async Task SendSubscribeUnsubscribe(List<MqttTopicFilter> addedSubscriptions, List<string> removedSubscriptions)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (removedSubscriptions != null && removedSubscriptions.Any())
|
||||
{
|
||||
await HandleSubscriptionExceptionAsync(exception).ConfigureAwait(false);
|
||||
await InternalClient.UnsubscribeAsync(removedSubscriptions.ToArray()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (addedSubscriptions != null && addedSubscriptions.Any())
|
||||
{
|
||||
await InternalClient.SubscribeAsync(addedSubscriptions.ToArray()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
await HandleSubscriptionExceptionAsync(exception).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,8 +570,26 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
if (_reconnectSubscriptions.Any())
|
||||
{
|
||||
var subscriptions = _reconnectSubscriptions.Select(i => new MqttTopicFilter { Topic = i.Key, QualityOfServiceLevel = i.Value });
|
||||
await _mqttClient.SubscribeAsync(subscriptions.ToArray()).ConfigureAwait(false);
|
||||
var subscriptions = _reconnectSubscriptions.Select(i => new MqttTopicFilter
|
||||
{
|
||||
Topic = i.Key,
|
||||
QualityOfServiceLevel = i.Value
|
||||
});
|
||||
|
||||
var topicFilters = new List<MqttTopicFilter>();
|
||||
|
||||
foreach (var sub in subscriptions)
|
||||
{
|
||||
topicFilters.Add(sub);
|
||||
|
||||
if (topicFilters.Count == Options.MaxTopicFiltersInSubscribeUnsubscribePackets)
|
||||
{
|
||||
await SendSubscribeUnsubscribe(topicFilters, null).ConfigureAwait(false);
|
||||
topicFilters.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
await SendSubscribeUnsubscribe(topicFilters, null).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
@@ -541,16 +609,16 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
}
|
||||
}
|
||||
|
||||
async Task<ReconnectionResult> ReconnectIfRequiredAsync()
|
||||
async Task<ReconnectionResult> ReconnectIfRequiredAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (_mqttClient.IsConnected)
|
||||
if (InternalClient.IsConnected)
|
||||
{
|
||||
return ReconnectionResult.StillConnected;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _mqttClient.ConnectAsync(Options.ClientOptions).ConfigureAwait(false);
|
||||
var result = await InternalClient.ConnectAsync(Options.ClientOptions, cancellationToken).ConfigureAwait(false);
|
||||
return result.IsSessionPresent ? ReconnectionResult.Recovered : ReconnectionResult.Reconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
@@ -576,7 +644,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
var cancellationToken = cancellationTokenSource.Token;
|
||||
_publishingCancellationToken = cancellationTokenSource;
|
||||
|
||||
Task.Run(() => PublishQueuedMessagesAsync(cancellationToken), cancellationToken).Forget(_logger);
|
||||
Task.Run(() => PublishQueuedMessagesAsync(cancellationToken), cancellationToken).RunInBackground(_logger);
|
||||
}
|
||||
|
||||
void StopPublishing()
|
||||
@@ -611,4 +679,4 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
return remainingTime < TimeSpan.Zero ? TimeSpan.Zero : remainingTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,13 @@
|
||||
using System;
|
||||
using System;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class ManagedMqttClientOptions : IManagedMqttClientOptions
|
||||
public sealed class ManagedMqttClientOptions : IManagedMqttClientOptions
|
||||
{
|
||||
public IMqttClientOptions ClientOptions { get; set; }
|
||||
|
||||
public bool AutoReconnect { get; set; } = true;
|
||||
|
||||
public TimeSpan AutoReconnectDelay { get; set; } = TimeSpan.FromSeconds(5);
|
||||
|
||||
public TimeSpan ConnectionCheckInterval { get; set; } = TimeSpan.FromSeconds(1);
|
||||
@@ -19,5 +17,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
public int MaxPendingMessages { get; set; } = int.MaxValue;
|
||||
|
||||
public MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; set; } = MqttPendingMessagesOverflowStrategy.DropNewMessage;
|
||||
|
||||
public int MaxTopicFiltersInSubscribeUnsubscribePackets { get; set; } = int.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Server;
|
||||
|
||||
@@ -69,18 +69,12 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
return this;
|
||||
}
|
||||
|
||||
public ManagedMqttClientOptionsBuilder WithAutoReconnect()
|
||||
public ManagedMqttClientOptionsBuilder WithMaxTopicFiltersInSubscribeUnsubscribePackets(int value)
|
||||
{
|
||||
_options.AutoReconnect = true;
|
||||
_options.MaxTopicFiltersInSubscribeUnsubscribePackets = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ManagedMqttClientOptionsBuilder WithoutAutoReconnect()
|
||||
{
|
||||
_options.AutoReconnect = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ManagedMqttClientOptions Build()
|
||||
{
|
||||
if (_clientOptionsBuilder != null)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using MQTTnet.Diagnostics;
|
||||
using System;
|
||||
using System;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Extensions.Rpc
|
||||
{
|
||||
public interface IMqttRpcClient : IDisposable
|
||||
{
|
||||
Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel);
|
||||
|
||||
Task<byte[]> ExecuteAsync(string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;net5.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' AND '$(SolutionName)' != 'MQTTnet.noUWP' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
<Product />
|
||||
<Company />
|
||||
<Authors />
|
||||
<PackageId />
|
||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
<DelaySign>false</DelaySign>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Extensions.Rpc.Options;
|
||||
using MQTTnet.Extensions.Rpc.Options.TopicGeneration;
|
||||
using MQTTnet.Protocol;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client.Subscribing;
|
||||
using MQTTnet.Implementations;
|
||||
|
||||
namespace MQTTnet.Extensions.Rpc
|
||||
{
|
||||
public sealed class MqttRpcClient : IDisposable
|
||||
public sealed class MqttRpcClient : IMqttRpcClient
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, TaskCompletionSource<byte[]>> _waitingCalls = new ConcurrentDictionary<string, TaskCompletionSource<byte[]>>();
|
||||
private readonly IMqttClient _mqttClient;
|
||||
private readonly IMqttRpcClientOptions _options;
|
||||
private readonly RpcAwareApplicationMessageReceivedHandler _applicationMessageReceivedHandler;
|
||||
readonly ConcurrentDictionary<string, TaskCompletionSource<byte[]>> _waitingCalls = new ConcurrentDictionary<string, TaskCompletionSource<byte[]>>();
|
||||
readonly IMqttClient _mqttClient;
|
||||
readonly IMqttRpcClientOptions _options;
|
||||
readonly RpcAwareApplicationMessageReceivedHandler _applicationMessageReceivedHandler;
|
||||
|
||||
[Obsolete("Use MqttRpcClient(IMqttClient mqttClient, IMqttRpcClientOptions options).")]
|
||||
public MqttRpcClient(IMqttClient mqttClient) : this(mqttClient, new MqttRpcClientOptions())
|
||||
@@ -35,30 +36,30 @@ namespace MQTTnet.Extensions.Rpc
|
||||
_mqttClient.ApplicationMessageReceivedHandler = _applicationMessageReceivedHandler;
|
||||
}
|
||||
|
||||
public Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
public async Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
{
|
||||
return ExecuteAsync(timeout, methodName, Encoding.UTF8.GetBytes(payload), qualityOfServiceLevel, CancellationToken.None);
|
||||
using (var timeoutToken = new CancellationTokenSource(timeout))
|
||||
{
|
||||
try
|
||||
{
|
||||
return await ExecuteAsync(methodName, payload, qualityOfServiceLevel, timeoutToken.Token).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException exception)
|
||||
{
|
||||
if (timeoutToken.IsCancellationRequested)
|
||||
{
|
||||
throw new MqttCommunicationTimedOutException(exception);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken)
|
||||
{
|
||||
return ExecuteAsync(timeout, methodName, Encoding.UTF8.GetBytes(payload), qualityOfServiceLevel, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
{
|
||||
return ExecuteAsync(timeout, methodName, payload, qualityOfServiceLevel, CancellationToken.None);
|
||||
}
|
||||
|
||||
public async Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken)
|
||||
public async Task<byte[]> ExecuteAsync(string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken)
|
||||
{
|
||||
if (methodName == null) throw new ArgumentNullException(nameof(methodName));
|
||||
|
||||
if (methodName.Contains("/") || methodName.Contains("+") || methodName.Contains("#"))
|
||||
{
|
||||
throw new ArgumentException("The method name cannot contain /, + or #.");
|
||||
}
|
||||
|
||||
if (!(_mqttClient.ApplicationMessageReceivedHandler is RpcAwareApplicationMessageReceivedHandler))
|
||||
{
|
||||
throw new InvalidOperationException("The application message received handler was modified.");
|
||||
@@ -89,66 +90,59 @@ namespace MQTTnet.Extensions.Rpc
|
||||
.WithTopic(requestTopic)
|
||||
.WithPayload(payload)
|
||||
.WithQualityOfServiceLevel(qualityOfServiceLevel)
|
||||
.WithResponseTopic(responseTopic)
|
||||
.Build();
|
||||
|
||||
try
|
||||
{
|
||||
var tcs = new TaskCompletionSource<byte[]>();
|
||||
if (!_waitingCalls.TryAdd(responseTopic, tcs))
|
||||
#if NET452
|
||||
var awaitable = new TaskCompletionSource<byte[]>();
|
||||
#else
|
||||
var awaitable = new TaskCompletionSource<byte[]>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
#endif
|
||||
|
||||
if (!_waitingCalls.TryAdd(responseTopic, awaitable))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
await _mqttClient.SubscribeAsync(responseTopic, qualityOfServiceLevel).ConfigureAwait(false);
|
||||
await _mqttClient.PublishAsync(requestMessage).ConfigureAwait(false);
|
||||
var subscribeOptions = new MqttClientSubscribeOptionsBuilder()
|
||||
.WithTopicFilter(responseTopic, qualityOfServiceLevel)
|
||||
.Build();
|
||||
|
||||
using (var timeoutCts = new CancellationTokenSource(timeout))
|
||||
using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token))
|
||||
await _mqttClient.SubscribeAsync(subscribeOptions, cancellationToken).ConfigureAwait(false);
|
||||
await _mqttClient.PublishAsync(requestMessage, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
using (cancellationToken.Register(() => { awaitable.TrySetCanceled(); }))
|
||||
{
|
||||
linkedCts.Token.Register(() =>
|
||||
{
|
||||
if (!tcs.Task.IsCompleted && !tcs.Task.IsFaulted && !tcs.Task.IsCanceled)
|
||||
{
|
||||
tcs.TrySetCanceled();
|
||||
}
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
var result = await tcs.Task.ConfigureAwait(false);
|
||||
timeoutCts.Cancel(false);
|
||||
return result;
|
||||
}
|
||||
catch (OperationCanceledException exception)
|
||||
{
|
||||
if (timeoutCts.IsCancellationRequested && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
throw new MqttCommunicationTimedOutException(exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return await awaitable.Task.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_waitingCalls.TryRemove(responseTopic, out _);
|
||||
|
||||
await _mqttClient.UnsubscribeAsync(responseTopic).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
{
|
||||
if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var tcs))
|
||||
if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var awaitable))
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
}
|
||||
|
||||
tcs.TrySetResult(eventArgs.ApplicationMessage.Payload);
|
||||
#if NET452
|
||||
Task.Run(() => awaitable.TrySetResult(eventArgs.ApplicationMessage.Payload));
|
||||
#else
|
||||
awaitable.TrySetResult(eventArgs.ApplicationMessage.Payload);
|
||||
#endif
|
||||
|
||||
return Task.FromResult(0);
|
||||
// Set this message to handled to that other code can avoid execution etc.
|
||||
eventArgs.IsHandled = true;
|
||||
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -163,4 +157,4 @@ namespace MQTTnet.Extensions.Rpc
|
||||
_waitingCalls.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Extensions.Rpc
|
||||
{
|
||||
public static class MqttRpcClientExtensions
|
||||
{
|
||||
public static Task<byte[]> ExecuteAsync(this IMqttRpcClient client, TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
var buffer = Encoding.UTF8.GetBytes(payload ?? string.Empty);
|
||||
|
||||
return client.ExecuteAsync(timeout, methodName, buffer, qualityOfServiceLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace MQTTnet.Extensions.Rpc.Options
|
||||
{
|
||||
public class MqttRpcClientOptions : IMqttRpcClientOptions
|
||||
public sealed class MqttRpcClientOptions : IMqttRpcClientOptions
|
||||
{
|
||||
public IMqttRpcClientTopicGenerationStrategy TopicGenerationStrategy { get; set; } = new DefaultMqttRpcClientTopicGenerationStrategy();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ using System;
|
||||
|
||||
namespace MQTTnet.Extensions.Rpc.Options
|
||||
{
|
||||
public class MqttRpcClientOptionsBuilder
|
||||
public sealed class MqttRpcClientOptionsBuilder
|
||||
{
|
||||
IMqttRpcClientTopicGenerationStrategy _topicGenerationStrategy = new DefaultMqttRpcClientTopicGenerationStrategy();
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace MQTTnet.Extensions.Rpc.Options
|
||||
{
|
||||
public class MqttRpcTopicPair
|
||||
public sealed class MqttRpcTopicPair
|
||||
{
|
||||
public string RequestTopic { get; set; }
|
||||
|
||||
|
||||
+6
-1
@@ -2,10 +2,15 @@
|
||||
|
||||
namespace MQTTnet.Extensions.Rpc.Options.TopicGeneration
|
||||
{
|
||||
public class DefaultMqttRpcClientTopicGenerationStrategy : IMqttRpcClientTopicGenerationStrategy
|
||||
public sealed class DefaultMqttRpcClientTopicGenerationStrategy : IMqttRpcClientTopicGenerationStrategy
|
||||
{
|
||||
public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context)
|
||||
{
|
||||
if (context.MethodName.Contains("/") || context.MethodName.Contains("+") || context.MethodName.Contains("#"))
|
||||
{
|
||||
throw new ArgumentException("The method name cannot contain /, + or #.");
|
||||
}
|
||||
|
||||
var requestTopic = $"MQTTnet.RPC/{Guid.NewGuid():N}/{context.MethodName}";
|
||||
var responseTopic = requestTopic + "/response";
|
||||
|
||||
|
||||
@@ -3,10 +3,18 @@ using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Extensions.Rpc.Options.TopicGeneration
|
||||
{
|
||||
public class TopicGenerationContext
|
||||
public sealed class TopicGenerationContext
|
||||
{
|
||||
public string MethodName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the quality of service level.
|
||||
/// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message.
|
||||
/// There are 3 QoS levels in MQTT:
|
||||
/// - At most once (0): Message gets delivered no time, once or multiple times.
|
||||
/// - At least once (1): Message gets delivered at least once (one time or more often).
|
||||
/// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once).
|
||||
/// </summary>
|
||||
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
|
||||
|
||||
public IMqttClient MqttClient { get; set; }
|
||||
|
||||
@@ -4,9 +4,9 @@ using MQTTnet.Client.Receiving;
|
||||
|
||||
namespace MQTTnet.Extensions.Rpc
|
||||
{
|
||||
public class RpcAwareApplicationMessageReceivedHandler : IMqttApplicationMessageReceivedHandler
|
||||
public sealed class RpcAwareApplicationMessageReceivedHandler : IMqttApplicationMessageReceivedHandler
|
||||
{
|
||||
private readonly Func<MqttApplicationMessageReceivedEventArgs, Task> _handleReceivedApplicationMessageAsync;
|
||||
readonly Func<MqttApplicationMessageReceivedEventArgs, Task> _handleReceivedApplicationMessageAsync;
|
||||
|
||||
public RpcAwareApplicationMessageReceivedHandler(
|
||||
IMqttApplicationMessageReceivedHandler originalHandler,
|
||||
@@ -20,12 +20,13 @@ namespace MQTTnet.Extensions.Rpc
|
||||
|
||||
public async Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
{
|
||||
// First try to check if there is a pending RPC call!
|
||||
await _handleReceivedApplicationMessageAsync(eventArgs).ConfigureAwait(false);
|
||||
|
||||
if (OriginalHandler != null)
|
||||
{
|
||||
await OriginalHandler.HandleApplicationMessageReceivedAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await _handleReceivedApplicationMessageAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;net5.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' AND '$(SolutionName)' != 'MQTTnet.noUWP' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
<Product />
|
||||
<Company />
|
||||
<Authors />
|
||||
<PackageId />
|
||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
<DelaySign>false</DelaySign>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
|
||||
@@ -16,14 +16,14 @@ using WebSocket4Net;
|
||||
|
||||
namespace MQTTnet.Extensions.WebSocket4Net
|
||||
{
|
||||
public class WebSocket4NetMqttChannel : IMqttChannel
|
||||
public sealed class WebSocket4NetMqttChannel : IMqttChannel
|
||||
{
|
||||
private readonly BlockingCollection<byte> _receiveBuffer = new BlockingCollection<byte>();
|
||||
readonly BlockingCollection<byte> _receiveBuffer = new BlockingCollection<byte>();
|
||||
|
||||
private readonly IMqttClientOptions _clientOptions;
|
||||
private readonly MqttClientWebSocketOptions _webSocketOptions;
|
||||
readonly IMqttClientOptions _clientOptions;
|
||||
readonly MqttClientWebSocketOptions _webSocketOptions;
|
||||
|
||||
private WebSocket _webSocket;
|
||||
WebSocket _webSocket;
|
||||
|
||||
public WebSocket4NetMqttChannel(IMqttClientOptions clientOptions, MqttClientWebSocketOptions webSocketOptions)
|
||||
{
|
||||
@@ -166,12 +166,12 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
||||
_webSocket = null;
|
||||
}
|
||||
|
||||
private void OnError(object sender, ErrorEventArgs e)
|
||||
static void OnError(object sender, ErrorEventArgs e)
|
||||
{
|
||||
System.Diagnostics.Debug.Write(e.Exception.ToString());
|
||||
}
|
||||
|
||||
private void OnDataReceived(object sender, DataReceivedEventArgs e)
|
||||
void OnDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
foreach (var @byte in e.Data)
|
||||
{
|
||||
@@ -179,7 +179,7 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ConnectInternalAsync(CancellationToken cancellationToken)
|
||||
async Task ConnectInternalAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_webSocket.Error += OnError;
|
||||
_webSocket.DataReceived += OnDataReceived;
|
||||
@@ -201,7 +201,9 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
||||
_webSocket.Opened += SuccessHandler;
|
||||
_webSocket.Error += ErrorHandler;
|
||||
|
||||
#pragma warning disable AsyncFixer02 // Long-running or blocking operations inside an async method
|
||||
_webSocket.Open();
|
||||
#pragma warning restore AsyncFixer02 // Long-running or blocking operations inside an async method
|
||||
|
||||
var exception = await MqttTaskTimeout.WaitAsync(c =>
|
||||
{
|
||||
|
||||
@@ -4,10 +4,11 @@ using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using System;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
|
||||
namespace MQTTnet.Extensions.WebSocket4Net
|
||||
{
|
||||
public class WebSocket4NetMqttClientAdapterFactory : IMqttClientAdapterFactory
|
||||
public sealed class WebSocket4NetMqttClientAdapterFactory : IMqttClientAdapterFactory
|
||||
{
|
||||
readonly IMqttNetLogger _logger;
|
||||
|
||||
@@ -23,14 +24,22 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
||||
switch (options.ChannelOptions)
|
||||
{
|
||||
case MqttClientTcpOptions _:
|
||||
{
|
||||
return new MqttChannelAdapter(new MqttTcpChannel(options), new MqttPacketFormatterAdapter(options.ProtocolVersion), _logger);
|
||||
}
|
||||
{
|
||||
return new MqttChannelAdapter(
|
||||
new MqttTcpChannel(options),
|
||||
new MqttPacketFormatterAdapter(options.ProtocolVersion),
|
||||
options.PacketInspector,
|
||||
_logger);
|
||||
}
|
||||
|
||||
case MqttClientWebSocketOptions webSocketOptions:
|
||||
{
|
||||
return new MqttChannelAdapter(new WebSocket4NetMqttChannel(options, webSocketOptions), new MqttPacketFormatterAdapter(options.ProtocolVersion), _logger);
|
||||
}
|
||||
{
|
||||
return new MqttChannelAdapter(
|
||||
new WebSocket4NetMqttChannel(options, webSocketOptions),
|
||||
new MqttPacketFormatterAdapter(options.ProtocolVersion),
|
||||
options.PacketInspector,
|
||||
_logger);
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
|
||||
-134
@@ -1,134 +0,0 @@
|
||||
using MQTTnet.Client;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Security.Authentication;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Serializer;
|
||||
using WebSocket4Net;
|
||||
|
||||
namespace MQTTnet.TestApp.NetCore
|
||||
{
|
||||
public class WebSocket4NetMqttClientAdapterFactory : IMqttClientAdapterFactory
|
||||
{
|
||||
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetChildLogger logger)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
if (!(options.ChannelOptions is MqttClientWebSocketOptions))
|
||||
{
|
||||
throw new NotSupportedException("Only WebSocket connections are supported.");
|
||||
}
|
||||
|
||||
return new MqttChannelAdapter(new WebSocket4NetMqttChannel(options), new MqttPacketSerializer(), logger);
|
||||
}
|
||||
|
||||
private class WebSocket4NetMqttChannel : IMqttChannel
|
||||
{
|
||||
private readonly BlockingCollection<byte> _receiveBuffer = new BlockingCollection<byte>();
|
||||
|
||||
private readonly IMqttClientOptions _clientOptions;
|
||||
private WebSocket4Net.WebSocket _webSocket;
|
||||
|
||||
public WebSocket4NetMqttChannel(IMqttClientOptions clientOptions)
|
||||
{
|
||||
_clientOptions = clientOptions ?? throw new ArgumentNullException(nameof(clientOptions));
|
||||
}
|
||||
|
||||
public string Endpoint { get; } = "";
|
||||
|
||||
public Task ConnectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var channelOptions = (MqttClientWebSocketOptions)_clientOptions.ChannelOptions;
|
||||
|
||||
var uri = "ws://" + channelOptions.Uri;
|
||||
var sslProtocols = SslProtocols.None;
|
||||
|
||||
if (channelOptions.TlsOptions.UseTls)
|
||||
{
|
||||
uri = "wss://" + channelOptions.Uri;
|
||||
sslProtocols = SslProtocols.Tls12;
|
||||
}
|
||||
|
||||
var subProtocol = channelOptions.SubProtocols.FirstOrDefault() ?? string.Empty;
|
||||
|
||||
_webSocket = new WebSocket4Net.WebSocket(uri, subProtocol, sslProtocols: sslProtocols);
|
||||
_webSocket.DataReceived += OnDataReceived;
|
||||
_webSocket.Open();
|
||||
SpinWait.SpinUntil(() => _webSocket.State == WebSocketState.Open, _clientOptions.CommunicationTimeout);
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task DisconnectAsync()
|
||||
{
|
||||
if (_webSocket != null)
|
||||
{
|
||||
_webSocket.DataReceived -= OnDataReceived;
|
||||
_webSocket.Close();
|
||||
SpinWait.SpinUntil(() => _webSocket.State == WebSocketState.Closed, _clientOptions.CommunicationTimeout);
|
||||
}
|
||||
|
||||
_webSocket = null;
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
var readBytes = 0;
|
||||
while (count > 0 && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
byte @byte;
|
||||
if (readBytes == 0)
|
||||
{
|
||||
// Block until at lease one byte was received.
|
||||
@byte = _receiveBuffer.Take(cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_receiveBuffer.TryTake(out @byte))
|
||||
{
|
||||
return Task.FromResult(readBytes);
|
||||
}
|
||||
}
|
||||
|
||||
buffer[offset] = @byte;
|
||||
offset++;
|
||||
count--;
|
||||
readBytes++;
|
||||
}
|
||||
|
||||
return Task.FromResult(readBytes);
|
||||
}
|
||||
|
||||
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
_webSocket.Send(buffer, offset, count);
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_webSocket != null)
|
||||
{
|
||||
_webSocket.DataReceived -= OnDataReceived;
|
||||
_webSocket.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDataReceived(object sender, WebSocket4Net.DataReceivedEventArgs e)
|
||||
{
|
||||
foreach (var @byte in e.Data)
|
||||
{
|
||||
_receiveBuffer.Add(@byte);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-ef": {
|
||||
"version": "3.1.4",
|
||||
"commands": [
|
||||
"dotnet-ef"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace MQTTnet.Server.Configuration
|
||||
{
|
||||
public class CertificateSettingsModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Path to certificate.
|
||||
/// </summary>
|
||||
public string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Password of certificate.
|
||||
/// </summary>
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read certificate file.
|
||||
/// </summary>
|
||||
public byte[] ReadCertificate()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Path) || string.IsNullOrWhiteSpace(Path))
|
||||
{
|
||||
throw new FileNotFoundException("No path set");
|
||||
}
|
||||
|
||||
if (!File.Exists(Path))
|
||||
{
|
||||
throw new FileNotFoundException($"Could not find Certificate in path: {Path}");
|
||||
}
|
||||
|
||||
return File.ReadAllBytes(Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
namespace MQTTnet.Server.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// MQTT settings Model
|
||||
/// </summary>
|
||||
public class MqttSettingsModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Set default connection timeout in seconds
|
||||
/// </summary>
|
||||
public int CommunicationTimeout { get; set; } = 15;
|
||||
|
||||
/// <summary>
|
||||
/// Set 0 to disable connection backlogging
|
||||
/// </summary>
|
||||
public int ConnectionBacklog { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enable support for persistent sessions
|
||||
/// </summary>
|
||||
public bool EnablePersistentSessions { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Listen Settings
|
||||
/// </summary>
|
||||
public TcpEndPointModel TcpEndPoint { get; set; } = new TcpEndPointModel();
|
||||
|
||||
/// <summary>
|
||||
/// Encryption Listen Settings
|
||||
/// </summary>
|
||||
public TcpEndPointModel EncryptedTcpEndPoint { get; set; } = new TcpEndPointModel();
|
||||
|
||||
/// <summary>
|
||||
/// Settings for the Web Socket endpoint.
|
||||
/// </summary>
|
||||
public WebSocketEndPointModel WebSocketEndPoint { get; set; } = new WebSocketEndPointModel();
|
||||
|
||||
/// <summary>
|
||||
/// Set limit for max pending messages per client
|
||||
/// </summary>
|
||||
public int MaxPendingMessagesPerClient { get; set; } = 250;
|
||||
|
||||
/// <summary>
|
||||
/// The settings for retained messages.
|
||||
/// </summary>
|
||||
public RetainedApplicationMessagesModel RetainedApplicationMessages { get; set; } = new RetainedApplicationMessagesModel();
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables the MQTTnet internal logging.
|
||||
/// </summary>
|
||||
public bool EnableDebugLogging { get; set; } = false;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace MQTTnet.Server.Configuration
|
||||
{
|
||||
public static class PathHelper
|
||||
{
|
||||
public static string ExpandPath(string path)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var uri = new Uri(path, UriKind.RelativeOrAbsolute);
|
||||
if (!uri.IsAbsoluteUri)
|
||||
{
|
||||
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, path);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
namespace MQTTnet.Server.Configuration
|
||||
{
|
||||
public class RetainedApplicationMessagesModel
|
||||
{
|
||||
public bool Persist { get; set; } = false;
|
||||
|
||||
public int WriteInterval { get; set; } = 10;
|
||||
|
||||
public string Path { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MQTTnet.Server.Configuration
|
||||
{
|
||||
public class ScriptingSettingsModel
|
||||
{
|
||||
public string ScriptsPath { get; set; }
|
||||
|
||||
public List<string> IncludePaths { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
|
||||
namespace MQTTnet.Server.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Listen Entry Settings Model
|
||||
/// </summary>
|
||||
public class TcpEndPointModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Certificate settings.
|
||||
/// </summary>
|
||||
public CertificateSettingsModel Certificate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enabled / Disable
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Listen Address
|
||||
/// </summary>
|
||||
public string IPv4 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Listen Address
|
||||
/// </summary>
|
||||
public string IPv6 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Listen Port
|
||||
/// </summary>
|
||||
public int Port { get; set; } = 1883;
|
||||
|
||||
/// <summary>
|
||||
/// Read IPv4
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool TryReadIPv4(out IPAddress address)
|
||||
{
|
||||
if (IPv4 == "*")
|
||||
{
|
||||
address = IPAddress.Any;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IPv4 == "localhost")
|
||||
{
|
||||
address = IPAddress.Loopback;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IPv4 == "disable")
|
||||
{
|
||||
address = IPAddress.None;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IPAddress.TryParse(IPv4, out var ip))
|
||||
{
|
||||
address = ip;
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new System.Exception($"Could not parse IPv4 address: {IPv4}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read IPv6
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool TryReadIPv6(out IPAddress address)
|
||||
{
|
||||
if (IPv6 == "*")
|
||||
{
|
||||
address = IPAddress.IPv6Any;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IPv6 == "localhost")
|
||||
{
|
||||
address = IPAddress.IPv6Loopback;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IPv6 == "disable")
|
||||
{
|
||||
address = IPAddress.None;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IPAddress.TryParse(IPv6, out var ip))
|
||||
{
|
||||
address = ip;
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new System.Exception($"Could not parse IPv6 address: {IPv6}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MQTTnet.Server.Configuration
|
||||
{
|
||||
public class WebSocketEndPointModel
|
||||
{
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
public string Path { get; set; } = "/mqtt";
|
||||
|
||||
public int ReceiveBufferSize { get; set; } = 4096;
|
||||
|
||||
public int KeepAliveInterval { get; set; } = 120;
|
||||
|
||||
public List<string> AllowedOrigins { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MQTTnet.Server.Mqtt;
|
||||
using MQTTnet.Server.Status;
|
||||
|
||||
namespace MQTTnet.Server.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
public class ClientsController : Controller
|
||||
{
|
||||
private readonly MqttServerService _mqttServerService;
|
||||
|
||||
public ClientsController(MqttServerService mqttServerService)
|
||||
{
|
||||
_mqttServerService = mqttServerService ?? throw new ArgumentNullException(nameof(mqttServerService));
|
||||
}
|
||||
|
||||
[Route("api/v1/clients")]
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<IList<IMqttClientStatus>>> GetClients()
|
||||
{
|
||||
return new ObjectResult(await _mqttServerService.GetClientStatusAsync());
|
||||
}
|
||||
|
||||
[Route("api/v1/clients/{clientId}")]
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<IMqttClientStatus>> GetClient(string clientId)
|
||||
{
|
||||
clientId = HttpUtility.UrlDecode(clientId);
|
||||
|
||||
var client = (await _mqttServerService.GetClientStatusAsync()).FirstOrDefault(c => c.ClientId == clientId);
|
||||
if (client == null)
|
||||
{
|
||||
return new StatusCodeResult((int)HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
return new ObjectResult(client);
|
||||
}
|
||||
|
||||
[Route("api/v1/clients/{clientId}")]
|
||||
[HttpDelete]
|
||||
public async Task<ActionResult> DeleteClient(string clientId)
|
||||
{
|
||||
clientId = HttpUtility.UrlDecode(clientId);
|
||||
|
||||
var client = (await _mqttServerService.GetClientStatusAsync()).FirstOrDefault(c => c.ClientId == clientId);
|
||||
if (client == null)
|
||||
{
|
||||
return new StatusCodeResult((int)HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
await client.DisconnectAsync();
|
||||
return StatusCode((int)HttpStatusCode.NoContent);
|
||||
}
|
||||
|
||||
[Route("api/v1/clients/{clientId}/statistics")]
|
||||
[HttpDelete]
|
||||
public async Task<ActionResult> DeleteClientStatistics(string clientId)
|
||||
{
|
||||
clientId = HttpUtility.UrlDecode(clientId);
|
||||
|
||||
var client = (await _mqttServerService.GetClientStatusAsync()).FirstOrDefault(c => c.ClientId == clientId);
|
||||
if (client == null)
|
||||
{
|
||||
return new StatusCodeResult((int)HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
client.ResetStatistics();
|
||||
return StatusCode((int)HttpStatusCode.NoContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server.Mqtt;
|
||||
|
||||
namespace MQTTnet.Server.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
public class MessagesController : Controller
|
||||
{
|
||||
private readonly MqttServerService _mqttServerService;
|
||||
|
||||
public MessagesController(MqttServerService mqttServerService)
|
||||
{
|
||||
_mqttServerService = mqttServerService ?? throw new ArgumentNullException(nameof(mqttServerService));
|
||||
}
|
||||
|
||||
[Route("api/v1/messages")]
|
||||
[HttpPost]
|
||||
public async Task<ActionResult> PostMessage(MqttApplicationMessage message)
|
||||
{
|
||||
await _mqttServerService.PublishAsync(message);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[Route("api/v1/messages/{*topic}")]
|
||||
[HttpPost]
|
||||
public async Task<ActionResult> PostMessage(string topic, int qosLevel = 0)
|
||||
{
|
||||
byte[] payload;
|
||||
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
await HttpContext.Request.Body.CopyToAsync(memoryStream);
|
||||
payload = memoryStream.ToArray();
|
||||
}
|
||||
|
||||
var message = new MqttApplicationMessageBuilder()
|
||||
.WithTopic(topic)
|
||||
.WithPayload(payload)
|
||||
.WithQualityOfServiceLevel((MqttQualityOfServiceLevel)qosLevel)
|
||||
.Build();
|
||||
|
||||
return await PostMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MQTTnet.Server.Mqtt;
|
||||
|
||||
namespace MQTTnet.Server.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
public class RetainedApplicationMessagesController : Controller
|
||||
{
|
||||
private readonly MqttServerService _mqttServerService;
|
||||
|
||||
public RetainedApplicationMessagesController(MqttServerService mqttServerService)
|
||||
{
|
||||
_mqttServerService = mqttServerService ?? throw new ArgumentNullException(nameof(mqttServerService));
|
||||
}
|
||||
|
||||
[Route("api/v1/retainedApplicationMessages")]
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<IList<MqttApplicationMessage>>> GetRetainedApplicationMessages()
|
||||
{
|
||||
return new ObjectResult(await _mqttServerService.GetRetainedApplicationMessagesAsync());
|
||||
}
|
||||
|
||||
[Route("api/v1/retainedApplicationMessages/{topic}")]
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<MqttApplicationMessage>> GetRetainedApplicationMessage(string topic)
|
||||
{
|
||||
topic = HttpUtility.UrlDecode(topic);
|
||||
|
||||
var applicationMessage = (await _mqttServerService.GetRetainedApplicationMessagesAsync()).FirstOrDefault(c => c.Topic == topic);
|
||||
if (applicationMessage == null)
|
||||
{
|
||||
return new StatusCodeResult((int)HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
return new ObjectResult(applicationMessage);
|
||||
}
|
||||
|
||||
[Route("api/v1/retainedApplicationMessages")]
|
||||
[HttpDelete]
|
||||
public async Task<ActionResult> DeleteRetainedApplicationMessages()
|
||||
{
|
||||
await _mqttServerService.ClearRetainedApplicationMessagesAsync();
|
||||
return StatusCode((int)HttpStatusCode.NoContent);
|
||||
}
|
||||
|
||||
[Route("api/v1/retainedApplicationMessages/{topic}")]
|
||||
[HttpDelete]
|
||||
public async Task<ActionResult> DeleteRetainedApplicationMessage(string topic)
|
||||
{
|
||||
topic = HttpUtility.UrlDecode(topic);
|
||||
|
||||
await _mqttServerService.PublishAsync(new MqttApplicationMessageBuilder().WithTopic(topic).WithRetainFlag().Build());
|
||||
return StatusCode((int)HttpStatusCode.NoContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MQTTnet.Server.Scripting;
|
||||
using MQTTnet.Server.Web;
|
||||
|
||||
namespace MQTTnet.Server.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
public class ScriptsController : Controller
|
||||
{
|
||||
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||
|
||||
public ScriptsController(PythonScriptHostService pythonScriptHostService)
|
||||
{
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
}
|
||||
|
||||
[Route("api/v1/scripts")]
|
||||
[HttpGet]
|
||||
public ActionResult<List<string>> GetScriptUids()
|
||||
{
|
||||
return _pythonScriptHostService.GetScriptUids();
|
||||
}
|
||||
|
||||
[Route("api/v1/scripts/uid")]
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<string>> GetScript(string uid)
|
||||
{
|
||||
var script = await _pythonScriptHostService.ReadScriptAsync(uid, HttpContext.RequestAborted);
|
||||
if (script == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Content(script);
|
||||
}
|
||||
|
||||
[Route("api/v1/scripts/uid")]
|
||||
[HttpPost]
|
||||
public Task PostScript(string uid)
|
||||
{
|
||||
var code = HttpContext.Request.ReadBodyAsString();
|
||||
return _pythonScriptHostService.WriteScriptAsync(uid, code, CancellationToken.None);
|
||||
}
|
||||
|
||||
[Route("api/v1/scripts/uid")]
|
||||
[HttpDelete]
|
||||
public Task DeleteScript(string uid)
|
||||
{
|
||||
return _pythonScriptHostService.DeleteScriptAsync(uid);
|
||||
}
|
||||
|
||||
[Route("api/v1/scripts/initialize")]
|
||||
[HttpPost]
|
||||
public Task PostInitializeScripts()
|
||||
{
|
||||
return _pythonScriptHostService.TryInitializeScriptsAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
|
||||
namespace MQTTnet.Server.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
public class ServerController : Controller
|
||||
{
|
||||
[Route("api/v1/server/version")]
|
||||
[HttpGet]
|
||||
public ActionResult<string> GetVersion()
|
||||
{
|
||||
var fileVersion = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location);
|
||||
return fileVersion.ProductVersion.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MQTTnet.Server.Mqtt;
|
||||
using MQTTnet.Server.Status;
|
||||
|
||||
namespace MQTTnet.Server.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
public class SessionsController : Controller
|
||||
{
|
||||
private readonly MqttServerService _mqttServerService;
|
||||
|
||||
public SessionsController(MqttServerService mqttServerService)
|
||||
{
|
||||
_mqttServerService = mqttServerService ?? throw new ArgumentNullException(nameof(mqttServerService));
|
||||
}
|
||||
|
||||
[Route("api/v1/sessions")]
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<IList<IMqttSessionStatus>>> GetSessions()
|
||||
{
|
||||
return new ObjectResult(await _mqttServerService.GetSessionStatusAsync());
|
||||
}
|
||||
|
||||
[Route("api/v1/sessions/{clientId}")]
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<IMqttClientStatus>> GetSession(string clientId)
|
||||
{
|
||||
clientId = HttpUtility.UrlDecode(clientId);
|
||||
|
||||
var session = (await _mqttServerService.GetSessionStatusAsync()).FirstOrDefault(c => c.ClientId == clientId);
|
||||
if (session == null)
|
||||
{
|
||||
return new StatusCodeResult((int)HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
return new ObjectResult(session);
|
||||
}
|
||||
|
||||
[Route("api/v1/sessions/{clientId}")]
|
||||
[HttpDelete]
|
||||
public async Task<ActionResult> DeleteSession(string clientId)
|
||||
{
|
||||
clientId = HttpUtility.UrlDecode(clientId);
|
||||
|
||||
var session = (await _mqttServerService.GetSessionStatusAsync()).FirstOrDefault(c => c.ClientId == clientId);
|
||||
if (session == null)
|
||||
{
|
||||
return new StatusCodeResult((int)HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
await session.DeleteAsync();
|
||||
return StatusCode((int)HttpStatusCode.NoContent);
|
||||
}
|
||||
|
||||
[Route("api/v1/sessions/{clientId}/pendingApplicationMessages")]
|
||||
[HttpDelete]
|
||||
public async Task<ActionResult> DeletePendingApplicationMessages(string clientId)
|
||||
{
|
||||
clientId = HttpUtility.UrlDecode(clientId);
|
||||
|
||||
var session = (await _mqttServerService.GetSessionStatusAsync()).FirstOrDefault(c => c.ClientId == clientId);
|
||||
if (session == null)
|
||||
{
|
||||
return new StatusCodeResult((int)HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
await session.ClearPendingApplicationMessagesAsync();
|
||||
return StatusCode((int)HttpStatusCode.NoContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
# MQTTnet
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 MQTTnet Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
# Swashbuckle
|
||||
Copyright (c) 2013, Richard Morris
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
|
||||
|
||||
# IronPython
|
||||
Copyright (c) .NET Foundation and Contributors
|
||||
All Rights Reserved
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
|
||||
|
||||
You must give any other recipients of the Work or Derivative Works a copy of this License; and
|
||||
|
||||
|
||||
You must cause any modified files to carry prominent notices stating that You changed the files; and
|
||||
|
||||
|
||||
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
|
||||
|
||||
|
||||
If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
|
||||
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
//using MQTTnet.Diagnostics;
|
||||
//using System;
|
||||
|
||||
//namespace MQTTnet.Server.Logging
|
||||
//{
|
||||
// public class MqttNetChildLoggerWrapper : IMqttNetChildLogger
|
||||
// {
|
||||
// private readonly MqttNetLoggerWrapper _logger;
|
||||
// private readonly string _source;
|
||||
|
||||
// public MqttNetChildLoggerWrapper(string source, MqttNetLoggerWrapper logger)
|
||||
// {
|
||||
// _logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
// _source = source;
|
||||
// }
|
||||
|
||||
// public IMqttNetLogger CreateChildLogger(string source = null)
|
||||
// {
|
||||
// return _logger.CreateChildLogger(source);
|
||||
// }
|
||||
|
||||
// public void Verbose(string message, params object[] parameters)
|
||||
// {
|
||||
// _logger.Publish(MqttNetLogLevel.Verbose, _source, message, parameters, null);
|
||||
// }
|
||||
|
||||
// public void Info(string message, params object[] parameters)
|
||||
// {
|
||||
// _logger.Publish(MqttNetLogLevel.Info, _source, message, parameters, null);
|
||||
// }
|
||||
|
||||
// public void Warning(Exception exception, string message, params object[] parameters)
|
||||
// {
|
||||
// _logger.Publish(MqttNetLogLevel.Warning, _source, message, parameters, exception);
|
||||
// }
|
||||
|
||||
// public void Error(Exception exception, string message, params object[] parameters)
|
||||
// {
|
||||
// _logger.Publish(MqttNetLogLevel.Error, _source, message, parameters, exception);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -1,64 +0,0 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Diagnostics;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace MQTTnet.Server.Logging
|
||||
{
|
||||
public sealed class MqttNetLoggerWrapper : IMqttNetLogger
|
||||
{
|
||||
readonly ILogger<MqttServer> _logger;
|
||||
|
||||
public MqttNetLoggerWrapper(ILogger<MqttServer> logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
|
||||
|
||||
public IMqttNetScopedLogger CreateScopedLogger(string source)
|
||||
{
|
||||
return new MqttNetScopedLogger(this, source);
|
||||
}
|
||||
|
||||
public void Publish(MqttNetLogLevel level, string source, string message, object[] parameters, Exception exception)
|
||||
{
|
||||
var convertedLogLevel = ConvertLogLevel(level);
|
||||
_logger.Log(convertedLogLevel, exception, message, parameters);
|
||||
|
||||
var logMessagePublishedEvent = LogMessagePublished;
|
||||
if (logMessagePublishedEvent != null)
|
||||
{
|
||||
var logMessage = new MqttNetLogMessage
|
||||
{
|
||||
Timestamp = DateTime.UtcNow,
|
||||
ThreadId = Thread.CurrentThread.ManagedThreadId,
|
||||
Source = source,
|
||||
Level = level,
|
||||
Message = message,
|
||||
Exception = exception
|
||||
};
|
||||
|
||||
logMessagePublishedEvent.Invoke(this, new MqttNetLogMessagePublishedEventArgs(logMessage));
|
||||
}
|
||||
}
|
||||
|
||||
public void Publish(MqttNetLogLevel logLevel, string message, object[] parameters, Exception exception)
|
||||
{
|
||||
Publish(logLevel, null, message, parameters, exception);
|
||||
}
|
||||
|
||||
static LogLevel ConvertLogLevel(MqttNetLogLevel logLevel)
|
||||
{
|
||||
switch (logLevel)
|
||||
{
|
||||
case MqttNetLogLevel.Error: return LogLevel.Error;
|
||||
case MqttNetLogLevel.Warning: return LogLevel.Warning;
|
||||
case MqttNetLogLevel.Info: return LogLevel.Information;
|
||||
case MqttNetLogLevel.Verbose: return LogLevel.Debug;
|
||||
}
|
||||
|
||||
return LogLevel.Debug;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
|
||||
<AssemblyName>MQTTnet.Server</AssemblyName>
|
||||
<RootNamespace>MQTTnet.Server</RootNamespace>
|
||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||
<Company />
|
||||
<Product>MQTTnet</Product>
|
||||
<Description>Standalone MQTT server with Python scripting backend.</Description>
|
||||
<Authors>The authors of MQTTnet.Server.</Authors>
|
||||
<PackageId />
|
||||
<SignAssembly>false</SignAssembly>
|
||||
<DelaySign>false</DelaySign>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Version>3.0.11</Version>
|
||||
<TypeScriptToolsVersion>3.3</TypeScriptToolsVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<StartupObject>MQTTnet.Server.Program</StartupObject>
|
||||
<UserSecretsId>c564f0de-28b4-45bf-b726-4d665d705653</UserSecretsId>
|
||||
<Copyright>(c) Christian Kratky 2016-2020</Copyright>
|
||||
<PackageProjectUrl>https://github.com/chkr1011/MQTTnet</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/chkr1011/MQTTnet</RepositoryUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile></DocumentationFile>
|
||||
<WarningsAsErrors>NU1605</WarningsAsErrors>
|
||||
<NoWarn>1701;1702</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="appsettings.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="IronPython" Version="2.7.10" />
|
||||
<PackageReference Include="IronPython.StdLib" Version="2.7.10" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.5" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.5.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.5.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj" />
|
||||
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="LICENSE">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="README.md">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scripts\README.md">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scripts\00_sample.py">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Web\authorization_handler.py">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Web\wwwroot\Hello.txt">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Web\wwwroot\mqtt_Test.html">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Web\wwwroot\pahoJS_Test.html">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\PublishProfiles\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,48 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Server.Configuration;
|
||||
using MQTTnet.Server.Logging;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
public class CustomMqttFactory
|
||||
{
|
||||
private readonly MqttFactory _mqttFactory;
|
||||
|
||||
public CustomMqttFactory(MqttSettingsModel settings, ILogger<MqttServer> logger)
|
||||
{
|
||||
if (settings == null) throw new ArgumentNullException(nameof(settings));
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
// It is important to avoid injecting the logger wrapper to ensure that no
|
||||
// unused log messages are generated by the MQTTnet library. Debug logging
|
||||
// has a huge performance impact.
|
||||
if (settings.EnableDebugLogging)
|
||||
{
|
||||
var mqttNetLogger = new MqttNetLoggerWrapper(logger);
|
||||
_mqttFactory = new MqttFactory(mqttNetLogger);
|
||||
|
||||
logger.LogWarning("Debug logging is enabled. Performance of MQTTnet Server is decreased!");
|
||||
}
|
||||
else
|
||||
{
|
||||
_mqttFactory = new MqttFactory();
|
||||
}
|
||||
|
||||
Logger = _mqttFactory.DefaultLogger;
|
||||
|
||||
}
|
||||
|
||||
public IMqttNetLogger Logger { get; }
|
||||
|
||||
public IMqttServer CreateMqttServer(List<IMqttServerAdapter> adapters)
|
||||
{
|
||||
if (adapters == null) throw new ArgumentNullException(nameof(adapters));
|
||||
|
||||
return _mqttFactory.CreateMqttServer(adapters);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using IronPython.Runtime;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server.Scripting;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
public class MqttApplicationMessageInterceptor : IMqttServerApplicationMessageInterceptor
|
||||
{
|
||||
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MqttApplicationMessageInterceptor(PythonScriptHostService pythonScriptHostService, ILogger<MqttApplicationMessageInterceptor> logger)
|
||||
{
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public Task InterceptApplicationMessagePublishAsync(MqttApplicationMessageInterceptorContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
// This might be not set when a message was published by the server instead of a client.
|
||||
context.SessionItems.TryGetValue(MqttServerConnectionValidator.WrappedSessionItemsKey, out var sessionItems);
|
||||
|
||||
var pythonContext = new PythonDictionary
|
||||
{
|
||||
{ "client_id", context.ClientId },
|
||||
{ "session_items", sessionItems },
|
||||
{ "retain", context.ApplicationMessage.Retain },
|
||||
{ "accept_publish", context.AcceptPublish },
|
||||
{ "close_connection", context.CloseConnection },
|
||||
{ "topic", context.ApplicationMessage.Topic },
|
||||
{ "qos", (int)context.ApplicationMessage.QualityOfServiceLevel }
|
||||
};
|
||||
|
||||
_pythonScriptHostService.InvokeOptionalFunction("on_intercept_application_message", pythonContext);
|
||||
|
||||
context.AcceptPublish = (bool)pythonContext.get("accept_publish", context.AcceptPublish);
|
||||
context.CloseConnection = (bool)pythonContext.get("close_connection", context.CloseConnection);
|
||||
context.ApplicationMessage.Topic = (string)pythonContext.get("topic", context.ApplicationMessage.Topic);
|
||||
context.ApplicationMessage.QualityOfServiceLevel = (MqttQualityOfServiceLevel)(int)pythonContext.get("qos", (int)context.ApplicationMessage.QualityOfServiceLevel);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Error while intercepting application message.");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using IronPython.Runtime;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Server.Scripting;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
public class MqttClientConnectedHandler : IMqttServerClientConnectedHandler
|
||||
{
|
||||
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MqttClientConnectedHandler(PythonScriptHostService pythonScriptHostService, ILogger<MqttClientConnectedHandler> logger)
|
||||
{
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public Task HandleClientConnectedAsync(MqttServerClientConnectedEventArgs eventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pythonEventArgs = new PythonDictionary
|
||||
{
|
||||
{ "client_id", eventArgs.ClientId }
|
||||
};
|
||||
|
||||
_pythonScriptHostService.InvokeOptionalFunction("on_client_connected", pythonEventArgs);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Error while handling client connected event.");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using IronPython.Runtime;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Server.Scripting;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
public class MqttClientDisconnectedHandler : IMqttServerClientDisconnectedHandler
|
||||
{
|
||||
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MqttClientDisconnectedHandler(PythonScriptHostService pythonScriptHostService, ILogger<MqttClientDisconnectedHandler> logger)
|
||||
{
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public Task HandleClientDisconnectedAsync(MqttServerClientDisconnectedEventArgs eventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pythonEventArgs = new PythonDictionary
|
||||
{
|
||||
{ "client_id", eventArgs.ClientId },
|
||||
{ "type", PythonConvert.Pythonfy(eventArgs.DisconnectType) }
|
||||
};
|
||||
|
||||
_pythonScriptHostService.InvokeOptionalFunction("on_client_disconnected", pythonEventArgs);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Error while handling client disconnected event.");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using IronPython.Runtime;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Server.Scripting;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
public class MqttClientSubscribedTopicHandler : IMqttServerClientSubscribedTopicHandler
|
||||
{
|
||||
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MqttClientSubscribedTopicHandler(PythonScriptHostService pythonScriptHostService, ILogger<MqttClientSubscribedTopicHandler> logger)
|
||||
{
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public Task HandleClientSubscribedTopicAsync(MqttServerClientSubscribedTopicEventArgs eventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pythonEventArgs = new PythonDictionary
|
||||
{
|
||||
{ "client_id", eventArgs.ClientId },
|
||||
{ "topic", eventArgs.TopicFilter.Topic },
|
||||
{ "qos", (int)eventArgs.TopicFilter.QualityOfServiceLevel }
|
||||
};
|
||||
|
||||
_pythonScriptHostService.InvokeOptionalFunction("on_client_subscribed_topic", pythonEventArgs);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Error while handling client subscribed topic event.");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using IronPython.Runtime;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Server.Scripting;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
public class MqttClientUnsubscribedTopicHandler : IMqttServerClientUnsubscribedTopicHandler
|
||||
{
|
||||
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MqttClientUnsubscribedTopicHandler(PythonScriptHostService pythonScriptHostService, ILogger<MqttClientUnsubscribedTopicHandler> logger)
|
||||
{
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public Task HandleClientUnsubscribedTopicAsync(MqttServerClientUnsubscribedTopicEventArgs eventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pythonEventArgs = new PythonDictionary
|
||||
{
|
||||
{ "client_id", eventArgs.ClientId },
|
||||
{ "topic", eventArgs.TopicFilter }
|
||||
};
|
||||
|
||||
_pythonScriptHostService.InvokeOptionalFunction("on_client_unsubscribed_topic", pythonEventArgs);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Error while handling client unsubscribed topic event.");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using IronPython.Runtime;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server.Scripting;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
public class MqttServerConnectionValidator : IMqttServerConnectionValidator
|
||||
{
|
||||
public const string WrappedSessionItemsKey = "WRAPPED_ITEMS";
|
||||
|
||||
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MqttServerConnectionValidator(PythonScriptHostService pythonScriptHostService, ILogger<MqttServerConnectionValidator> logger)
|
||||
{
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public Task ValidateConnectionAsync(MqttConnectionValidatorContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sessionItems = new PythonDictionary();
|
||||
|
||||
var pythonContext = new PythonDictionary
|
||||
{
|
||||
{ "endpoint", context.Endpoint },
|
||||
{ "is_secure_connection", context.IsSecureConnection },
|
||||
{ "client_id", context.ClientId },
|
||||
{ "username", context.Username },
|
||||
{ "password", context.Password },
|
||||
{ "raw_password", new Bytes(context.RawPassword ?? new byte[0]) },
|
||||
{ "clean_session", context.CleanSession},
|
||||
{ "authentication_method", context.AuthenticationMethod},
|
||||
{ "authentication_data", new Bytes(context.AuthenticationData ?? new byte[0]) },
|
||||
{ "session_items", sessionItems },
|
||||
|
||||
{ "result", PythonConvert.Pythonfy(context.ReasonCode) }
|
||||
};
|
||||
|
||||
_pythonScriptHostService.InvokeOptionalFunction("on_validate_client_connection", pythonContext);
|
||||
|
||||
context.ReasonCode = PythonConvert.ParseEnum<MqttConnectReasonCode>((string)pythonContext["result"]);
|
||||
|
||||
context.SessionItems[WrappedSessionItemsKey] = sessionItems;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Error while validating client connection.");
|
||||
|
||||
context.ReasonCode = MqttConnectReasonCode.UnspecifiedError;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,267 +0,0 @@
|
||||
using IronPython.Runtime;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.AspNetCore;
|
||||
using MQTTnet.Client.Publishing;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server.Configuration;
|
||||
using MQTTnet.Server.Scripting;
|
||||
using MQTTnet.Server.Status;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.WebSockets;
|
||||
using System.Security.Authentication;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
public class MqttServerService
|
||||
{
|
||||
readonly ILogger<MqttServerService> _logger;
|
||||
|
||||
readonly MqttSettingsModel _settings;
|
||||
readonly MqttApplicationMessageInterceptor _mqttApplicationMessageInterceptor;
|
||||
readonly MqttServerStorage _mqttServerStorage;
|
||||
readonly MqttClientConnectedHandler _mqttClientConnectedHandler;
|
||||
readonly MqttClientDisconnectedHandler _mqttClientDisconnectedHandler;
|
||||
readonly MqttClientSubscribedTopicHandler _mqttClientSubscribedTopicHandler;
|
||||
readonly MqttClientUnsubscribedTopicHandler _mqttClientUnsubscribedTopicHandler;
|
||||
readonly MqttServerConnectionValidator _mqttConnectionValidator;
|
||||
readonly IMqttServer _mqttServer;
|
||||
readonly MqttSubscriptionInterceptor _mqttSubscriptionInterceptor;
|
||||
readonly MqttUnsubscriptionInterceptor _mqttUnsubscriptionInterceptor;
|
||||
readonly PythonScriptHostService _pythonScriptHostService;
|
||||
readonly MqttWebSocketServerAdapter _webSocketServerAdapter;
|
||||
|
||||
public MqttServerService(
|
||||
MqttSettingsModel mqttSettings,
|
||||
CustomMqttFactory mqttFactory,
|
||||
MqttClientConnectedHandler mqttClientConnectedHandler,
|
||||
MqttClientDisconnectedHandler mqttClientDisconnectedHandler,
|
||||
MqttClientSubscribedTopicHandler mqttClientSubscribedTopicHandler,
|
||||
MqttClientUnsubscribedTopicHandler mqttClientUnsubscribedTopicHandler,
|
||||
MqttServerConnectionValidator mqttConnectionValidator,
|
||||
MqttSubscriptionInterceptor mqttSubscriptionInterceptor,
|
||||
MqttUnsubscriptionInterceptor mqttUnsubscriptionInterceptor,
|
||||
MqttApplicationMessageInterceptor mqttApplicationMessageInterceptor,
|
||||
MqttServerStorage mqttServerStorage,
|
||||
PythonScriptHostService pythonScriptHostService,
|
||||
ILogger<MqttServerService> logger)
|
||||
{
|
||||
_settings = mqttSettings ?? throw new ArgumentNullException(nameof(mqttSettings));
|
||||
_mqttClientConnectedHandler = mqttClientConnectedHandler ?? throw new ArgumentNullException(nameof(mqttClientConnectedHandler));
|
||||
_mqttClientDisconnectedHandler = mqttClientDisconnectedHandler ?? throw new ArgumentNullException(nameof(mqttClientDisconnectedHandler));
|
||||
_mqttClientSubscribedTopicHandler = mqttClientSubscribedTopicHandler ?? throw new ArgumentNullException(nameof(mqttClientSubscribedTopicHandler));
|
||||
_mqttClientUnsubscribedTopicHandler = mqttClientUnsubscribedTopicHandler ?? throw new ArgumentNullException(nameof(mqttClientUnsubscribedTopicHandler));
|
||||
_mqttConnectionValidator = mqttConnectionValidator ?? throw new ArgumentNullException(nameof(mqttConnectionValidator));
|
||||
_mqttSubscriptionInterceptor = mqttSubscriptionInterceptor ?? throw new ArgumentNullException(nameof(mqttSubscriptionInterceptor));
|
||||
_mqttUnsubscriptionInterceptor = mqttUnsubscriptionInterceptor ?? throw new ArgumentNullException(nameof(mqttUnsubscriptionInterceptor));
|
||||
_mqttApplicationMessageInterceptor = mqttApplicationMessageInterceptor ?? throw new ArgumentNullException(nameof(mqttApplicationMessageInterceptor));
|
||||
_mqttServerStorage = mqttServerStorage ?? throw new ArgumentNullException(nameof(mqttServerStorage));
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
_webSocketServerAdapter = new MqttWebSocketServerAdapter(mqttFactory.Logger);
|
||||
|
||||
var adapters = new List<IMqttServerAdapter>
|
||||
{
|
||||
new MqttTcpServerAdapter(mqttFactory.Logger)
|
||||
{
|
||||
TreatSocketOpeningErrorAsWarning = true // Opening other ports than for HTTP is not allows in Azure App Services.
|
||||
},
|
||||
_webSocketServerAdapter
|
||||
};
|
||||
|
||||
_mqttServer = mqttFactory.CreateMqttServer(adapters);
|
||||
}
|
||||
|
||||
public void Configure()
|
||||
{
|
||||
_pythonScriptHostService.RegisterProxyObject("publish", new Action<PythonDictionary>(Publish));
|
||||
|
||||
_mqttServerStorage.Configure();
|
||||
|
||||
_mqttServer.ClientConnectedHandler = _mqttClientConnectedHandler;
|
||||
_mqttServer.ClientDisconnectedHandler = _mqttClientDisconnectedHandler;
|
||||
_mqttServer.ClientSubscribedTopicHandler = _mqttClientSubscribedTopicHandler;
|
||||
_mqttServer.ClientUnsubscribedTopicHandler = _mqttClientUnsubscribedTopicHandler;
|
||||
|
||||
_mqttServer.StartAsync(CreateMqttServerOptions()).GetAwaiter().GetResult();
|
||||
|
||||
_logger.LogInformation("MQTT server started.");
|
||||
}
|
||||
|
||||
public Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
|
||||
{
|
||||
return _webSocketServerAdapter.RunWebSocketConnectionAsync(webSocket, httpContext);
|
||||
}
|
||||
|
||||
public Task<IList<IMqttClientStatus>> GetClientStatusAsync()
|
||||
{
|
||||
return _mqttServer.GetClientStatusAsync();
|
||||
}
|
||||
|
||||
public Task<IList<IMqttSessionStatus>> GetSessionStatusAsync()
|
||||
{
|
||||
return _mqttServer.GetSessionStatusAsync();
|
||||
}
|
||||
|
||||
public Task ClearRetainedApplicationMessagesAsync()
|
||||
{
|
||||
return _mqttServer.ClearRetainedApplicationMessagesAsync();
|
||||
}
|
||||
|
||||
public Task<IList<MqttApplicationMessage>> GetRetainedApplicationMessagesAsync()
|
||||
{
|
||||
return _mqttServer.GetRetainedApplicationMessagesAsync();
|
||||
}
|
||||
|
||||
public Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage)
|
||||
{
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
return _mqttServer.PublishAsync(applicationMessage);
|
||||
}
|
||||
|
||||
void Publish(PythonDictionary parameters)
|
||||
{
|
||||
try
|
||||
{
|
||||
var applicationMessageBuilder = new MqttApplicationMessageBuilder()
|
||||
.WithTopic((string)parameters.get("topic", null))
|
||||
.WithRetainFlag((bool)parameters.get("retain", false))
|
||||
.WithQualityOfServiceLevel((MqttQualityOfServiceLevel)(int)parameters.get("qos", 0));
|
||||
|
||||
var payload = parameters.get("payload", null);
|
||||
byte[] binaryPayload;
|
||||
|
||||
if (payload == null)
|
||||
{
|
||||
binaryPayload = new byte[0];
|
||||
}
|
||||
else if (payload is string stringPayload)
|
||||
{
|
||||
binaryPayload = Encoding.UTF8.GetBytes(stringPayload);
|
||||
}
|
||||
else if (payload is ByteArray byteArray)
|
||||
{
|
||||
binaryPayload = byteArray.ToArray();
|
||||
}
|
||||
else if (payload is IEnumerable<int> intArray)
|
||||
{
|
||||
binaryPayload = intArray.Select(Convert.ToByte).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException("Payload type not supported.");
|
||||
}
|
||||
|
||||
applicationMessageBuilder = applicationMessageBuilder
|
||||
.WithPayload(binaryPayload);
|
||||
|
||||
var applicationMessage = applicationMessageBuilder.Build();
|
||||
|
||||
_mqttServer.PublishAsync(applicationMessage).GetAwaiter().GetResult();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Error while publishing application message from server.");
|
||||
}
|
||||
}
|
||||
|
||||
IMqttServerOptions CreateMqttServerOptions()
|
||||
{
|
||||
var options = new MqttServerOptionsBuilder()
|
||||
.WithMaxPendingMessagesPerClient(_settings.MaxPendingMessagesPerClient)
|
||||
.WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(_settings.CommunicationTimeout))
|
||||
.WithConnectionValidator(_mqttConnectionValidator)
|
||||
.WithApplicationMessageInterceptor(_mqttApplicationMessageInterceptor)
|
||||
.WithSubscriptionInterceptor(_mqttSubscriptionInterceptor)
|
||||
.WithUnsubscriptionInterceptor(_mqttUnsubscriptionInterceptor)
|
||||
.WithStorage(_mqttServerStorage);
|
||||
|
||||
// Configure unencrypted connections
|
||||
if (_settings.TcpEndPoint.Enabled)
|
||||
{
|
||||
options.WithDefaultEndpoint();
|
||||
|
||||
if (_settings.TcpEndPoint.TryReadIPv4(out var address4))
|
||||
{
|
||||
options.WithDefaultEndpointBoundIPAddress(address4);
|
||||
}
|
||||
|
||||
if (_settings.TcpEndPoint.TryReadIPv6(out var address6))
|
||||
{
|
||||
options.WithDefaultEndpointBoundIPV6Address(address6);
|
||||
}
|
||||
|
||||
if (_settings.TcpEndPoint.Port > 0)
|
||||
{
|
||||
options.WithDefaultEndpointPort(_settings.TcpEndPoint.Port);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
options.WithoutDefaultEndpoint();
|
||||
}
|
||||
|
||||
// Configure encrypted connections
|
||||
if (_settings.EncryptedTcpEndPoint.Enabled)
|
||||
{
|
||||
options
|
||||
.WithEncryptedEndpoint()
|
||||
.WithEncryptionSslProtocol(SslProtocols.Tls12);
|
||||
|
||||
if (!string.IsNullOrEmpty(_settings.EncryptedTcpEndPoint?.Certificate?.Path))
|
||||
{
|
||||
IMqttServerCertificateCredentials certificateCredentials = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(_settings.EncryptedTcpEndPoint?.Certificate?.Password))
|
||||
{
|
||||
certificateCredentials = new MqttServerCertificateCredentials
|
||||
{
|
||||
Password = _settings.EncryptedTcpEndPoint.Certificate.Password
|
||||
};
|
||||
}
|
||||
|
||||
options.WithEncryptionCertificate(_settings.EncryptedTcpEndPoint.Certificate.ReadCertificate(), certificateCredentials);
|
||||
}
|
||||
|
||||
if (_settings.EncryptedTcpEndPoint.TryReadIPv4(out var address4))
|
||||
{
|
||||
options.WithEncryptedEndpointBoundIPAddress(address4);
|
||||
}
|
||||
|
||||
if (_settings.EncryptedTcpEndPoint.TryReadIPv6(out var address6))
|
||||
{
|
||||
options.WithEncryptedEndpointBoundIPV6Address(address6);
|
||||
}
|
||||
|
||||
if (_settings.EncryptedTcpEndPoint.Port > 0)
|
||||
{
|
||||
options.WithEncryptedEndpointPort(_settings.EncryptedTcpEndPoint.Port);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
options.WithoutEncryptedEndpoint();
|
||||
}
|
||||
|
||||
if (_settings.ConnectionBacklog > 0)
|
||||
{
|
||||
options.WithConnectionBacklog(_settings.ConnectionBacklog);
|
||||
}
|
||||
|
||||
if (_settings.EnablePersistentSessions)
|
||||
{
|
||||
options.WithPersistentSessions();
|
||||
}
|
||||
|
||||
return options.Build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Server.Configuration;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
public class MqttServerStorage : IMqttServerStorage
|
||||
{
|
||||
private readonly List<MqttApplicationMessage> _messages = new List<MqttApplicationMessage>();
|
||||
|
||||
private readonly MqttSettingsModel _mqttSettings;
|
||||
private readonly ILogger<MqttServerStorage> _logger;
|
||||
|
||||
private string _path;
|
||||
private bool _messagesHaveChanged;
|
||||
|
||||
public MqttServerStorage(MqttSettingsModel mqttSettings, ILogger<MqttServerStorage> logger)
|
||||
{
|
||||
_mqttSettings = mqttSettings ?? throw new ArgumentNullException(nameof(mqttSettings));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public void Configure()
|
||||
{
|
||||
if (_mqttSettings.RetainedApplicationMessages?.Persist != true ||
|
||||
string.IsNullOrEmpty(_mqttSettings.RetainedApplicationMessages.Path))
|
||||
{
|
||||
_logger.LogInformation("Persisting of retained application messages is disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
_path = PathHelper.ExpandPath(_mqttSettings.RetainedApplicationMessages.Path);
|
||||
|
||||
// The retained application messages are stored in a separate thread.
|
||||
// This is mandatory because writing them to a slow storage (like RaspberryPi SD card)
|
||||
// will slow down the whole message processing speed.
|
||||
Task.Run(SaveRetainedMessagesInternalAsync, CancellationToken.None);
|
||||
}
|
||||
|
||||
public Task SaveRetainedMessagesAsync(IList<MqttApplicationMessage> messages)
|
||||
{
|
||||
lock (_messages)
|
||||
{
|
||||
_messages.Clear();
|
||||
_messages.AddRange(messages);
|
||||
|
||||
_messagesHaveChanged = true;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task SaveRetainedMessagesInternalAsync()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(_mqttSettings.RetainedApplicationMessages.WriteInterval)).ConfigureAwait(false);
|
||||
|
||||
List<MqttApplicationMessage> messages;
|
||||
lock (_messages)
|
||||
{
|
||||
if (!_messagesHaveChanged)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
messages = new List<MqttApplicationMessage>(_messages);
|
||||
_messagesHaveChanged = false;
|
||||
}
|
||||
|
||||
var json = JsonConvert.SerializeObject(messages);
|
||||
await File.WriteAllTextAsync(_path, json, Encoding.UTF8).ConfigureAwait(false);
|
||||
|
||||
_logger.LogInformation($"{messages.Count} retained MQTT messages written.");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Error while writing retained MQTT messages.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IList<MqttApplicationMessage>> LoadRetainedMessagesAsync()
|
||||
{
|
||||
if (_mqttSettings.RetainedApplicationMessages?.Persist != true)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!File.Exists(_path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var json = await File.ReadAllTextAsync(_path).ConfigureAwait(false);
|
||||
var applicationMessages = JsonConvert.DeserializeObject<List<MqttApplicationMessage>>(json);
|
||||
|
||||
_logger.LogInformation($"{applicationMessages.Count} retained MQTT messages loaded.");
|
||||
|
||||
return applicationMessages;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogWarning(exception, "Error while loading persisted retained application messages.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using IronPython.Runtime;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Server.Scripting;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
public class MqttSubscriptionInterceptor : IMqttServerSubscriptionInterceptor
|
||||
{
|
||||
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MqttSubscriptionInterceptor(PythonScriptHostService pythonScriptHostService, ILogger<MqttSubscriptionInterceptor> logger)
|
||||
{
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public Task InterceptSubscriptionAsync(MqttSubscriptionInterceptorContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sessionItems = (PythonDictionary)context.SessionItems[MqttServerConnectionValidator.WrappedSessionItemsKey];
|
||||
|
||||
var pythonContext = new PythonDictionary
|
||||
{
|
||||
{ "client_id", context.ClientId },
|
||||
{ "session_items", sessionItems },
|
||||
{ "topic", context.TopicFilter.Topic },
|
||||
{ "qos", (int)context.TopicFilter.QualityOfServiceLevel },
|
||||
{ "accept_subscription", context.AcceptSubscription },
|
||||
{ "close_connection", context.CloseConnection }
|
||||
};
|
||||
|
||||
_pythonScriptHostService.InvokeOptionalFunction("on_intercept_subscription", pythonContext);
|
||||
|
||||
context.AcceptSubscription = (bool)pythonContext["accept_subscription"];
|
||||
context.CloseConnection = (bool)pythonContext["close_connection"];
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Error while intercepting subscription.");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using IronPython.Runtime;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Server.Scripting;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
public class MqttUnsubscriptionInterceptor : IMqttServerUnsubscriptionInterceptor
|
||||
{
|
||||
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MqttUnsubscriptionInterceptor(PythonScriptHostService pythonScriptHostService, ILogger<MqttUnsubscriptionInterceptor> logger)
|
||||
{
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public Task InterceptUnsubscriptionAsync(MqttUnsubscriptionInterceptorContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sessionItems = (PythonDictionary)context.SessionItems[MqttServerConnectionValidator.WrappedSessionItemsKey];
|
||||
|
||||
var pythonContext = new PythonDictionary
|
||||
{
|
||||
{ "client_id", context.ClientId },
|
||||
{ "session_items", sessionItems },
|
||||
{ "topic", context.Topic },
|
||||
{ "accept_unsubscription", context.AcceptUnsubscription },
|
||||
{ "close_connection", context.CloseConnection }
|
||||
};
|
||||
|
||||
_pythonScriptHostService.InvokeOptionalFunction("on_intercept_unsubscription", pythonContext);
|
||||
|
||||
context.AcceptUnsubscription = (bool)pythonContext["accept_unsubscription"];
|
||||
context.CloseConnection = (bool)pythonContext["close_connection"];
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Error while intercepting unsubscription.");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using MQTTnet.Server.Web;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
PrintLogo();
|
||||
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.ConfigureKestrel(serverOptions =>
|
||||
{
|
||||
})
|
||||
.UseWebRoot(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Web", "wwwroot"))
|
||||
.UseStartup<Startup>();
|
||||
}).Build().Run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine(exception);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintLogo()
|
||||
{
|
||||
Console.ResetColor();
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
const string LogoText =
|
||||
@"
|
||||
|
||||
███╗ ███╗ ██████╗ ████████╗████████╗███╗ ██╗███████╗████████╗ ███████╗███████╗██████╗ ██╗ ██╗███████╗██████╗
|
||||
████╗ ████║██╔═══██╗╚══██╔══╝╚══██╔══╝████╗ ██║██╔════╝╚══██╔══╝ ██╔════╝██╔════╝██╔══██╗██║ ██║██╔════╝██╔══██╗
|
||||
██╔████╔██║██║ ██║ ██║ ██║ ██╔██╗ ██║█████╗ ██║ ███████╗█████╗ ██████╔╝██║ ██║█████╗ ██████╔╝
|
||||
██║╚██╔╝██║██║▄▄ ██║ ██║ ██║ ██║╚██╗██║██╔══╝ ██║ ╚════██║██╔══╝ ██╔══██╗╚██╗ ██╔╝██╔══╝ ██╔══██╗
|
||||
██║ ╚═╝ ██║╚██████╔╝ ██║ ██║ ██║ ╚████║███████╗ ██║ ███████║███████╗██║ ██║ ╚████╔╝ ███████╗██║ ██║
|
||||
╚═╝ ╚═╝ ╚══▀▀═╝ ╚═╝ ╚═╝ ╚═╝ ╚═══╝╚══════╝ ╚═╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝
|
||||
|
||||
";
|
||||
|
||||
Console.WriteLine(LogoText);
|
||||
Console.ResetColor();
|
||||
Console.WriteLine();
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.WriteLine("The official MQTT server implementation of MQTTnet");
|
||||
Console.WriteLine("Copyright (c) 2017-2020 The MQTTnet Team");
|
||||
Console.WriteLine(@"https://github.com/chkr1011/MQTTnet");
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
|
||||
var fileVersion = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location);
|
||||
|
||||
Console.WriteLine($@"
|
||||
Version: {fileVersion.ProductVersion}
|
||||
License: MIT (read LICENSE file)
|
||||
Sponsoring: https://opencollective.com/mqttnet
|
||||
Support: https://github.com/chkr1011/MQTTnet/issues
|
||||
Docs: https://github.com/chkr1011/MQTTnet/wiki/MQTTnetServer
|
||||
");
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine(" ! THIS IS AN ALPHA VERSION! IT IS NOT RECOMMENDED TO USE IT FOR ANY DIFFERENT PURPOSE THAN TESTING OR EVALUATING!");
|
||||
Console.ResetColor();
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
# Starting portable version
|
||||
The portable version requires a local installation of the .net core runtime. With that runtime installed the server can be started via the following comand.
|
||||
|
||||
dotnet .\MQTTnet.Server.dll
|
||||
|
||||
# Starting self contained versions
|
||||
The self contained versions are fully portable versions including the .net core runtime. The server can be started using the contained executable files.
|
||||
|
||||
Windows: MQTTnet.Server.exe
|
||||
Linux: MQTTnet.Server (must be set to _executable_ first)
|
||||
@@ -1,47 +0,0 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MQTTnet.Server.Scripting.DataSharing
|
||||
{
|
||||
public class DataSharingService
|
||||
{
|
||||
readonly Dictionary<string, object> _storage = new Dictionary<string, object>();
|
||||
readonly PythonScriptHostService _pythonScriptHostService;
|
||||
readonly ILogger<DataSharingService> _logger;
|
||||
|
||||
public DataSharingService(PythonScriptHostService pythonScriptHostService, ILogger<DataSharingService> logger)
|
||||
{
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public void Configure()
|
||||
{
|
||||
_pythonScriptHostService.RegisterProxyObject("write_shared_data", new Action<string, object>(Write));
|
||||
_pythonScriptHostService.RegisterProxyObject("read_shared_data", new Func<string, object, object>(Read));
|
||||
}
|
||||
|
||||
public void Write(string key, object value)
|
||||
{
|
||||
lock (_storage)
|
||||
{
|
||||
_storage[key] = value;
|
||||
_logger.LogInformation($"Shared data with key '{key}' updated.");
|
||||
}
|
||||
}
|
||||
|
||||
public object Read(string key, object defaultValue)
|
||||
{
|
||||
lock (_storage)
|
||||
{
|
||||
if (!_storage.TryGetValue(key, out var value))
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using IronPython.Runtime;
|
||||
|
||||
namespace MQTTnet.Server.Scripting
|
||||
{
|
||||
public static class PythonConvert
|
||||
{
|
||||
public static object ToPython(object value)
|
||||
{
|
||||
if (value is PythonDictionary)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value is string)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value is int)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value is float)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value is bool)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value is IDictionary dictionary)
|
||||
{
|
||||
var pythonDictionary = new PythonDictionary();
|
||||
foreach (DictionaryEntry dictionaryEntry in dictionary)
|
||||
{
|
||||
pythonDictionary.Add(dictionaryEntry.Key, ToPython(dictionaryEntry.Value));
|
||||
}
|
||||
|
||||
return pythonDictionary;
|
||||
}
|
||||
|
||||
if (value is IEnumerable enumerable)
|
||||
{
|
||||
var pythonList = new List();
|
||||
foreach (var item in enumerable)
|
||||
{
|
||||
pythonList.Add(ToPython(item));
|
||||
}
|
||||
|
||||
return pythonList;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static string Pythonfy(Enum value)
|
||||
{
|
||||
return Pythonfy(value.ToString());
|
||||
}
|
||||
|
||||
public static string Pythonfy(string value)
|
||||
{
|
||||
var result = new StringBuilder();
|
||||
foreach (var @char in value)
|
||||
{
|
||||
if (char.IsUpper(@char) && result.Length > 0)
|
||||
{
|
||||
result.Append('_');
|
||||
}
|
||||
|
||||
result.Append(char.ToLowerInvariant(@char));
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
public static TEnum ParseEnum<TEnum>(string value) where TEnum : Enum
|
||||
{
|
||||
return (TEnum)Enum.Parse(typeof(TEnum), value.Replace("_", string.Empty), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace MQTTnet.Server.Scripting
|
||||
{
|
||||
public class PythonIOStream : Stream
|
||||
{
|
||||
readonly ILogger _logger;
|
||||
readonly Encoding _encoder = Encoding.UTF8;
|
||||
|
||||
public PythonIOStream(ILogger<PythonIOStream> logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var text = _encoder.GetString(buffer, offset, count);
|
||||
if (text.Equals(Environment.NewLine))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogDebug(text);
|
||||
}
|
||||
|
||||
public override bool CanRead { get; } = false;
|
||||
public override bool CanSeek { get; } = false;
|
||||
public override bool CanWrite { get; } = true;
|
||||
public override long Length { get; } = 0L;
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => throw new NotSupportedException();
|
||||
set => throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Scripting;
|
||||
using Microsoft.Scripting.Hosting;
|
||||
using MQTTnet.Server.Configuration;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Server.Scripting
|
||||
{
|
||||
public class PythonScriptHostService
|
||||
{
|
||||
readonly IDictionary<string, object> _proxyObjects = new ExpandoObject();
|
||||
readonly List<PythonScriptInstance> _scriptInstances = new List<PythonScriptInstance>();
|
||||
readonly string _scriptsPath;
|
||||
readonly ScriptingSettingsModel _scriptingSettings;
|
||||
readonly ILogger<PythonScriptHostService> _logger;
|
||||
readonly ScriptEngine _scriptEngine;
|
||||
|
||||
public PythonScriptHostService(ScriptingSettingsModel scriptingSettings, PythonIOStream pythonIOStream, ILogger<PythonScriptHostService> logger)
|
||||
{
|
||||
_scriptingSettings = scriptingSettings ?? throw new ArgumentNullException(nameof(scriptingSettings));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
_scriptEngine = IronPython.Hosting.Python.CreateEngine();
|
||||
_scriptEngine.Runtime.IO.SetOutput(pythonIOStream, Encoding.UTF8);
|
||||
|
||||
_scriptsPath = PathHelper.ExpandPath(scriptingSettings.ScriptsPath);
|
||||
}
|
||||
|
||||
public void Configure()
|
||||
{
|
||||
AddSearchPaths(_scriptEngine);
|
||||
|
||||
TryInitializeScriptsAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public void RegisterProxyObject(string name, object @object)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
if (@object == null) throw new ArgumentNullException(nameof(@object));
|
||||
|
||||
_proxyObjects.Add(name, @object);
|
||||
}
|
||||
|
||||
public void InvokeOptionalFunction(string name, object parameters)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
|
||||
lock (_scriptInstances)
|
||||
{
|
||||
foreach (var pythonScriptInstance in _scriptInstances)
|
||||
{
|
||||
try
|
||||
{
|
||||
pythonScriptInstance.InvokeOptionalFunction(name, parameters);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, $"Error while invoking function '{name}' at script '{pythonScriptInstance.Uid}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> GetScriptUids()
|
||||
{
|
||||
lock (_scriptInstances)
|
||||
{
|
||||
return _scriptInstances.Select(si => si.Uid).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public Task<string> ReadScriptAsync(string uid, CancellationToken cancellationToken)
|
||||
{
|
||||
if (uid == null) throw new ArgumentNullException(nameof(uid));
|
||||
|
||||
string path;
|
||||
|
||||
lock (_scriptInstances)
|
||||
{
|
||||
path = _scriptInstances.FirstOrDefault(si => si.Uid == uid)?.Path;
|
||||
}
|
||||
|
||||
if (path == null || !File.Exists(path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return File.ReadAllTextAsync(path, Encoding.UTF8, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task WriteScriptAsync(string uid, string code, CancellationToken cancellationToken)
|
||||
{
|
||||
var path = Path.Combine(_scriptsPath, uid + ".py");
|
||||
|
||||
await File.WriteAllTextAsync(path, code, Encoding.UTF8, cancellationToken).ConfigureAwait(false);
|
||||
await TryInitializeScriptsAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task DeleteScriptAsync(string uid)
|
||||
{
|
||||
var path = Path.Combine(_scriptsPath, uid + ".py");
|
||||
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
await TryInitializeScriptsAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task TryInitializeScriptsAsync()
|
||||
{
|
||||
lock (_scriptInstances)
|
||||
{
|
||||
foreach (var scriptInstance in _scriptInstances)
|
||||
{
|
||||
try
|
||||
{
|
||||
scriptInstance.InvokeOptionalFunction("destroy");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogWarning(exception, $"Error while unloading script '{scriptInstance.Uid}'.");
|
||||
}
|
||||
}
|
||||
|
||||
_scriptInstances.Clear();
|
||||
}
|
||||
|
||||
foreach (var path in Directory.GetFiles(_scriptsPath, "*.py", SearchOption.AllDirectories).OrderBy(file => file))
|
||||
{
|
||||
await TryInitializeScriptAsync(path).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
async Task TryInitializeScriptAsync(string path)
|
||||
{
|
||||
var uid = new FileInfo(path).Name.Replace(".py", string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
try
|
||||
{
|
||||
_logger.LogTrace($"Initializing Python script '{uid}'...");
|
||||
var code = await File.ReadAllTextAsync(path).ConfigureAwait(false);
|
||||
|
||||
var scriptInstance = CreateScriptInstance(uid, path, code);
|
||||
scriptInstance.InvokeOptionalFunction("initialize");
|
||||
|
||||
lock (_scriptInstances)
|
||||
{
|
||||
_scriptInstances.Add(scriptInstance);
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Initialized script '{uid}'.");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, $"Error while initializing script '{uid}'.");
|
||||
}
|
||||
}
|
||||
|
||||
PythonScriptInstance CreateScriptInstance(string uid, string path, string code)
|
||||
{
|
||||
var scriptScope = _scriptEngine.CreateScope();
|
||||
|
||||
var source = scriptScope.Engine.CreateScriptSourceFromString(code, SourceCodeKind.File);
|
||||
var compiledCode = source.Compile();
|
||||
|
||||
scriptScope.SetVariable("mqtt_net_server", _proxyObjects);
|
||||
compiledCode.Execute(scriptScope);
|
||||
|
||||
return new PythonScriptInstance(uid, path, scriptScope);
|
||||
}
|
||||
|
||||
void AddSearchPaths(ScriptEngine scriptEngine)
|
||||
{
|
||||
if (_scriptingSettings.IncludePaths?.Any() != true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var searchPaths = scriptEngine.GetSearchPaths();
|
||||
|
||||
foreach (var path in _scriptingSettings.IncludePaths)
|
||||
{
|
||||
var effectivePath = PathHelper.ExpandPath(path);
|
||||
|
||||
if (Directory.Exists(effectivePath))
|
||||
{
|
||||
searchPaths.Add(effectivePath);
|
||||
_logger.LogInformation($"Added Python lib path: {effectivePath}");
|
||||
}
|
||||
}
|
||||
|
||||
scriptEngine.SetSearchPaths(searchPaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
using IronPython.Runtime;
|
||||
using Microsoft.Scripting.Hosting;
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Server.Scripting
|
||||
{
|
||||
public class PythonScriptInstance
|
||||
{
|
||||
readonly ScriptScope _scriptScope;
|
||||
|
||||
public PythonScriptInstance(string uid, string path, ScriptScope scriptScope)
|
||||
{
|
||||
Uid = uid;
|
||||
Path = path;
|
||||
|
||||
_scriptScope = scriptScope;
|
||||
}
|
||||
|
||||
public string Uid { get; }
|
||||
|
||||
public string Path { get; }
|
||||
|
||||
public bool InvokeOptionalFunction(string name, params object[] parameters)
|
||||
{
|
||||
return InvokeOptionalFunction(name, parameters, out _);
|
||||
}
|
||||
|
||||
public bool InvokeOptionalFunction(string name, object[] parameters, out object result)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
|
||||
lock (_scriptScope)
|
||||
{
|
||||
if (!_scriptScope.Engine.Operations.TryGetMember(_scriptScope, name, out var member))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(member is PythonFunction function))
|
||||
{
|
||||
throw new Exception($"Member '{name}' is no Python function.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
result = _scriptScope.Engine.Operations.Invoke(function, parameters);
|
||||
return true;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var details = _scriptScope.Engine.GetService<ExceptionOperations>().FormatException(exception);
|
||||
var message = $"Error while invoking function '{name}'. " + Environment.NewLine + details;
|
||||
|
||||
throw new Exception(message, exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
import json
|
||||
|
||||
|
||||
def initialize():
|
||||
"""
|
||||
This function is invoked after the script file has been loaded.
|
||||
It will be executed only one time.
|
||||
"""
|
||||
|
||||
print("Hello World from sample script.")
|
||||
|
||||
|
||||
def destroy():
|
||||
"""
|
||||
This function is invoked when the script is unloaded due to a script file update etc.
|
||||
"""
|
||||
|
||||
print("Bye from sample script.")
|
||||
|
||||
|
||||
def on_validate_client_connection(context):
|
||||
"""
|
||||
This function is invoked whenever a client wants to connect. It can be used to validate the connection.
|
||||
"""
|
||||
|
||||
print(context)
|
||||
|
||||
# Access some custom data here which was inserted upon connect and may use it for validation.
|
||||
mqtt_net_server.write_shared_data(context["client_id"], {"custom_value_1": 1, "custom_value_2": True})
|
||||
|
||||
return
|
||||
|
||||
# Supported results:
|
||||
# * success
|
||||
# * unspecified_error
|
||||
# * malformed_packet
|
||||
# * protocol_error
|
||||
# * implementation_specific_error
|
||||
# * unsupported_protocol_version
|
||||
# * client_identifier_not_valid
|
||||
# * bad_user_name_or_password
|
||||
# * not_authorized
|
||||
# * server_unavailable
|
||||
# * server_busy
|
||||
# * banned
|
||||
# * bad_authentication_method
|
||||
# * topic_name_invalid
|
||||
# * packet_too_large
|
||||
# * quota_exceeded
|
||||
# * payload_format_invalid
|
||||
# * retain_not_supported
|
||||
# * qos_not_supported
|
||||
# * use_another_server
|
||||
# * server_moved
|
||||
# * connection_rate_exceeded
|
||||
|
||||
if context["client_id"] != "test_client":
|
||||
context["result"] = "bad_user_name_or_password"
|
||||
return
|
||||
|
||||
if context["username"] != "bud spencer":
|
||||
context["result"] = "bad_user_name_or_password"
|
||||
return
|
||||
|
||||
if context["password_string"] != "secret":
|
||||
context["result"] = "bad_user_name_or_password"
|
||||
|
||||
print(context)
|
||||
|
||||
|
||||
def on_intercept_subscription(context):
|
||||
"""
|
||||
This function is invoked whenever a client wants to subscribe to a topic.
|
||||
"""
|
||||
|
||||
print("Client '{client_id}' want's to subscribe to topic '{topic}'.".format(client_id=context["client_id"], topic=context["topic"]))
|
||||
|
||||
|
||||
def on_intercept_application_message(context):
|
||||
"""
|
||||
This function is invoked for every processed application message. It also allows modifying
|
||||
the message or cancel processing at all.
|
||||
"""
|
||||
|
||||
client_id = context["client_id"]
|
||||
|
||||
if client_id != None:
|
||||
shared_data = mqtt_net_server.read_shared_data(context["client_id"], {})
|
||||
print(shared_data)
|
||||
|
||||
if context["topic"] == "topic_with_response":
|
||||
|
||||
json_payload = {
|
||||
"hello": "world",
|
||||
"x": 1,
|
||||
"y": True,
|
||||
"z": None
|
||||
}
|
||||
|
||||
application_message = {
|
||||
"retain": False,
|
||||
"topic": "reply",
|
||||
"payload": json.dumps(json_payload)
|
||||
}
|
||||
|
||||
mqtt_net_server.publish(application_message)
|
||||
|
||||
print("Client '{client_id}' published topic '{topic}'.".format(client_id=context["client_id"], topic=context["topic"]))
|
||||
|
||||
|
||||
def on_client_connected(event_args):
|
||||
"""
|
||||
This function is called whenever a client has passed the validation is connected.
|
||||
"""
|
||||
|
||||
print("Client '{client_id}' is now connected.".format(client_id=event_args["client_id"]))
|
||||
|
||||
|
||||
def on_client_disconnected(event_args):
|
||||
"""
|
||||
This function is called whenever a client has disconnected.
|
||||
"""
|
||||
|
||||
print("Client '{client_id}' is now disconnected (type = {type}).".format(client_id=event_args["client_id"], type=event_args["type"]))
|
||||
|
||||
|
||||
def on_client_subscribed_topic(event_args):
|
||||
"""
|
||||
This function is called whenever a client has subscribed to a topic (when allowed).
|
||||
"""
|
||||
|
||||
print("Client '{client_id}' has subscribed to '{topic}'.".format(client_id=event_args["client_id"], topic=event_args["topic"]))
|
||||
|
||||
|
||||
def on_client_unsubscribed_topic(event_args):
|
||||
"""
|
||||
This function is called whenever a client has unsubscribed from a topic.
|
||||
"""
|
||||
|
||||
print("Client '{client_id}' has unsubscribed from '{topic}'.".format(client_id=event_args["client_id"], topic=event_args["topic"]))
|
||||
@@ -1,21 +0,0 @@
|
||||
# MQTT Scripts
|
||||
|
||||
This directory contains scripts which are loaded by the server and can be used to perform the following tasks.
|
||||
|
||||
* Validation of client connections via credentials, client IDs etc.
|
||||
* Manipulation of every processed message.
|
||||
* Validation of subscription attempts.
|
||||
* Publishing of custom application messages.
|
||||
|
||||
All scripts are loaded and _MQTTnet Server_ will invoke functions according to predefined naming conventions.
|
||||
If a function is implemented in multiple scripts the context will be moved throug all instances. This allows overriding of results or passing data to other (following) scripts.
|
||||
|
||||
The Python starndard library ships with _MQTTnet Server_. But it is possible to add custom paths with Python libraries.
|
||||
|
||||
```
|
||||
import sys
|
||||
sys.path.append(PATH_TO_LIBRARY)
|
||||
```
|
||||
|
||||
* All scripts must have the file extension _.py_.
|
||||
* All scripts are sorted alphabetically (A to Z) before being loaded and parsed.
|
||||
@@ -1,118 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using IronPython.Runtime;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Microsoft.Scripting;
|
||||
|
||||
namespace MQTTnet.Server.Web
|
||||
{
|
||||
public class AuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
|
||||
{
|
||||
private readonly ILogger<AuthenticationHandler> _logger;
|
||||
|
||||
public AuthenticationHandler(
|
||||
IOptionsMonitor<AuthenticationSchemeOptions> options,
|
||||
ILoggerFactory loggerFactory,
|
||||
UrlEncoder encoder,
|
||||
ISystemClock clock,
|
||||
ILogger<AuthenticationHandler> logger)
|
||||
: base(options, loggerFactory, encoder, clock)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
if (!Request.Headers.ContainsKey(HeaderNames.Authorization))
|
||||
{
|
||||
return AuthenticateResult.NoResult();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var headerValue = Request.Headers[HeaderNames.Authorization];
|
||||
var parsedHeaderValue = AuthenticationHeaderValue.Parse(Request.Headers[HeaderNames.Authorization]);
|
||||
|
||||
var scheme = parsedHeaderValue.Scheme;
|
||||
var parameter = parsedHeaderValue.Parameter;
|
||||
string username = null;
|
||||
string password = null;
|
||||
|
||||
if (scheme.Equals("Basic", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var credentialBytes = Convert.FromBase64String(parsedHeaderValue.Parameter);
|
||||
var credentials = Encoding.UTF8.GetString(credentialBytes).Split(':');
|
||||
username = credentials[0];
|
||||
password = credentials[1];
|
||||
}
|
||||
|
||||
var context = new PythonDictionary
|
||||
{
|
||||
["header_value"] = headerValue,
|
||||
["scheme"] = scheme,
|
||||
["parameter"] = parameter,
|
||||
["username"] = username,
|
||||
["password"] = password,
|
||||
["is_authenticated"] = false
|
||||
};
|
||||
|
||||
if (!ValidateUser(context))
|
||||
{
|
||||
return AuthenticateResult.Fail("Invalid credentials.");
|
||||
}
|
||||
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, context.get("username") as string ?? string.Empty)
|
||||
};
|
||||
|
||||
var identity = new ClaimsIdentity(claims, Scheme.Name);
|
||||
var principal = new ClaimsPrincipal(identity);
|
||||
var ticket = new AuthenticationTicket(principal, Scheme.Name);
|
||||
|
||||
return AuthenticateResult.Success(ticket);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogWarning("Error while authenticating user.", exception);
|
||||
|
||||
return AuthenticateResult.Fail(exception);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ValidateUser(PythonDictionary context)
|
||||
{
|
||||
var handlerPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Web", "authorization_handler.py");
|
||||
if (!File.Exists(handlerPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var code = File.ReadAllText(handlerPath);
|
||||
|
||||
var scriptEngine = IronPython.Hosting.Python.CreateEngine();
|
||||
//scriptEngine.Runtime.IO.SetOutput(new PythonIOStream(_logger.), Encoding.UTF8);
|
||||
|
||||
var scriptScope = scriptEngine.CreateScope();
|
||||
var scriptSource = scriptScope.Engine.CreateScriptSourceFromString(code, SourceCodeKind.File);
|
||||
var compiledCode = scriptSource.Compile();
|
||||
compiledCode.Execute(scriptScope);
|
||||
var function = scriptScope.Engine.Operations.GetMember<PythonFunction>(scriptScope, "handle_authenticate");
|
||||
scriptScope.Engine.Operations.Invoke(function, context);
|
||||
|
||||
return context.get("is_authenticated", false) as bool? == true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace MQTTnet.Server.Web
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
public static string ReadBodyAsString(this HttpRequest request)
|
||||
{
|
||||
if (request == null) throw new ArgumentNullException(nameof(request));
|
||||
|
||||
using (var reader = new StreamReader(request.Body, Encoding.UTF8))
|
||||
{
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,224 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.Scripting.Utils;
|
||||
using MQTTnet.AspNetCore;
|
||||
using MQTTnet.Server.Configuration;
|
||||
using MQTTnet.Server.Logging;
|
||||
using MQTTnet.Server.Mqtt;
|
||||
using MQTTnet.Server.Scripting;
|
||||
using MQTTnet.Server.Scripting.DataSharing;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Swashbuckle.AspNetCore.SwaggerUI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
||||
namespace MQTTnet.Server.Web
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.AddJsonFile("appsettings.json")
|
||||
.AddEnvironmentVariables();
|
||||
|
||||
Configuration = builder.Build();
|
||||
}
|
||||
|
||||
public IConfigurationRoot Configuration { get; }
|
||||
|
||||
public void Configure(
|
||||
IApplicationBuilder application,
|
||||
MqttServerService mqttServerService,
|
||||
PythonScriptHostService pythonScriptHostService,
|
||||
DataSharingService dataSharingService,
|
||||
MqttSettingsModel mqttSettings)
|
||||
{
|
||||
application.UseDefaultFiles();
|
||||
application.UseStaticFiles();
|
||||
|
||||
application.UseHsts();
|
||||
application.UseRouting();
|
||||
application.UseCors(x => x
|
||||
.AllowAnyOrigin()
|
||||
.AllowAnyMethod()
|
||||
.AllowAnyHeader());
|
||||
|
||||
application.UseAuthentication();
|
||||
application.UseAuthorization();
|
||||
|
||||
application.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
});
|
||||
|
||||
ConfigureWebSocketEndpoint(application, mqttServerService, mqttSettings);
|
||||
|
||||
dataSharingService.Configure();
|
||||
pythonScriptHostService.Configure();
|
||||
|
||||
mqttServerService.Configure();
|
||||
|
||||
application.UseSwagger(o => o.RouteTemplate = "/api/{documentName}/swagger.json");
|
||||
|
||||
application.UseSwaggerUI(o =>
|
||||
{
|
||||
o.RoutePrefix = "api";
|
||||
o.DocumentTitle = "MQTTnet.Server API";
|
||||
o.SwaggerEndpoint("/api/v1/swagger.json", "MQTTnet.Server API v1");
|
||||
o.DisplayRequestDuration();
|
||||
o.DocExpansion(DocExpansion.List);
|
||||
o.DefaultModelRendering(ModelRendering.Model);
|
||||
});
|
||||
}
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddCors();
|
||||
|
||||
services.AddControllers();
|
||||
|
||||
services.AddMvc()
|
||||
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
|
||||
.AddNewtonsoftJson(o =>
|
||||
{
|
||||
o.SerializerSettings.Converters.Add(new StringEnumConverter());
|
||||
});
|
||||
|
||||
ReadMqttSettings(services);
|
||||
|
||||
services.AddSingleton<PythonIOStream>();
|
||||
services.AddSingleton<PythonScriptHostService>();
|
||||
services.AddSingleton<DataSharingService>();
|
||||
|
||||
services.AddSingleton<MqttNetLoggerWrapper>();
|
||||
services.AddSingleton<CustomMqttFactory>();
|
||||
services.AddSingleton<MqttServerService>();
|
||||
services.AddSingleton<MqttServerStorage>();
|
||||
|
||||
services.AddSingleton<MqttClientConnectedHandler>();
|
||||
services.AddSingleton<MqttClientDisconnectedHandler>();
|
||||
services.AddSingleton<MqttClientSubscribedTopicHandler>();
|
||||
services.AddSingleton<MqttClientUnsubscribedTopicHandler>();
|
||||
services.AddSingleton<MqttServerConnectionValidator>();
|
||||
services.AddSingleton<MqttSubscriptionInterceptor>();
|
||||
services.AddSingleton<MqttUnsubscriptionInterceptor>();
|
||||
services.AddSingleton<MqttApplicationMessageInterceptor>();
|
||||
|
||||
services.AddSwaggerGen(c =>
|
||||
{
|
||||
var securityScheme = new OpenApiSecurityScheme
|
||||
{
|
||||
Scheme = "Basic",
|
||||
Name = HeaderNames.Authorization,
|
||||
Type = SecuritySchemeType.Http,
|
||||
In = ParameterLocation.Header
|
||||
};
|
||||
|
||||
c.AddSecurityDefinition("Swagger", securityScheme);
|
||||
|
||||
c.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||
{
|
||||
[securityScheme] = new List<string>()
|
||||
});
|
||||
|
||||
c.SwaggerDoc("v1", new OpenApiInfo
|
||||
{
|
||||
Title = "MQTTnet.Server API",
|
||||
Version = "v1",
|
||||
Description = "The public API for the MQTT broker MQTTnet.Server.",
|
||||
License = new OpenApiLicense
|
||||
{
|
||||
Name = "MIT",
|
||||
Url = new Uri("https://github.com/chkr1011/MQTTnet/blob/master/README.md")
|
||||
},
|
||||
Contact = new OpenApiContact
|
||||
{
|
||||
Name = "MQTTnet.Server",
|
||||
Email = string.Empty,
|
||||
Url = new Uri("https://github.com/chkr1011/MQTTnet")
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
services.AddAuthentication("Basic")
|
||||
.AddScheme<AuthenticationSchemeOptions, AuthenticationHandler>("Basic", null)
|
||||
.AddCookie();
|
||||
}
|
||||
|
||||
void ReadMqttSettings(IServiceCollection services)
|
||||
{
|
||||
var mqttSettings = new MqttSettingsModel();
|
||||
Configuration.Bind("MQTT", mqttSettings);
|
||||
services.AddSingleton(mqttSettings);
|
||||
|
||||
var scriptingSettings = new ScriptingSettingsModel();
|
||||
Configuration.Bind("Scripting", scriptingSettings);
|
||||
services.AddSingleton(scriptingSettings);
|
||||
}
|
||||
|
||||
static void ConfigureWebSocketEndpoint(
|
||||
IApplicationBuilder application,
|
||||
MqttServerService mqttServerService,
|
||||
MqttSettingsModel mqttSettings)
|
||||
{
|
||||
if (mqttSettings?.WebSocketEndPoint?.Enabled != true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(mqttSettings.WebSocketEndPoint.Path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var webSocketOptions = new WebSocketOptions
|
||||
{
|
||||
KeepAliveInterval = TimeSpan.FromSeconds(mqttSettings.WebSocketEndPoint.KeepAliveInterval),
|
||||
ReceiveBufferSize = mqttSettings.WebSocketEndPoint.ReceiveBufferSize
|
||||
};
|
||||
|
||||
if (mqttSettings.WebSocketEndPoint.AllowedOrigins?.Any() == true)
|
||||
{
|
||||
webSocketOptions.AllowedOrigins.AddRange(mqttSettings.WebSocketEndPoint.AllowedOrigins);
|
||||
}
|
||||
|
||||
application.UseWebSockets(webSocketOptions);
|
||||
|
||||
application.Use(async (context, next) =>
|
||||
{
|
||||
if (context.Request.Path == mqttSettings.WebSocketEndPoint.Path)
|
||||
{
|
||||
if (context.WebSockets.IsWebSocketRequest)
|
||||
{
|
||||
string subProtocol = null;
|
||||
if (context.Request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
|
||||
{
|
||||
subProtocol = MqttSubProtocolSelector.SelectSubProtocol(requestedSubProtocolValues);
|
||||
}
|
||||
|
||||
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(subProtocol).ConfigureAwait(false))
|
||||
{
|
||||
await mqttServerService.RunWebSocketConnectionAsync(webSocket, context).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await next().ConfigureAwait(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
def handle_authenticate(context):
|
||||
"""
|
||||
This function is invoked whenever a user tries to access protected HTTP resources.
|
||||
This function must exist and return a proper value. Otherwise the request is denied.
|
||||
All authentication data must be sent via using the "Authorization" header. This header
|
||||
will be parsed and all values will be exposed to the context.
|
||||
"""
|
||||
header_value = context["header_value"] # The untouched header value.
|
||||
|
||||
scheme = context["scheme"]
|
||||
parameter = context["parameter"]
|
||||
|
||||
username = context["username"] # Only set if proper "Basic" authorization is used.
|
||||
password = context["password"] # Only set if proper "Basic" authorization is used.
|
||||
|
||||
context["is_authenticated"] = True # Change this to _False_ in case of invalid credentials.
|
||||
|
||||
# Example for an API key with proper format.
|
||||
if header_value == "APIKey 123456":
|
||||
context["is_authenticated"] = True
|
||||
@@ -1 +0,0 @@
|
||||
World
|
||||
@@ -1,33 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>MQTT JS Example</title>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/mqtt@4.0.1/dist/mqtt.min.js" type="text/javascript"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var client = mqtt.connect('ws://localhost:80/mqtt')
|
||||
|
||||
client.on('connect', function () {
|
||||
client.subscribe('mqtt_subscription', function (err) {
|
||||
console.log("Subscribed");
|
||||
})
|
||||
})
|
||||
|
||||
client.on('message', function (topic, message) {
|
||||
console.log(topic);
|
||||
console.log(message.toString())
|
||||
})
|
||||
|
||||
function buttonClick() {
|
||||
client.publish("MQTT_JS_TOPIC", "payload");
|
||||
}
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<button onclick="buttonClick()">Publish</button>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,41 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>paho JS Example</title>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.js" type="text/javascript"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var mqttClient = new Paho.MQTT.Client("localhost", 80, "/mqtt", "pahoJS_client");
|
||||
mqttClient.onConnectionLost = onConnectionLost;
|
||||
mqttClient.onMessageArrived = onMessageArrived;
|
||||
mqttClient.connect({ onSuccess: onConnect });
|
||||
|
||||
function onConnect() {
|
||||
console.log("MQTT client connected");
|
||||
mqttClient.subscribe("pahoJS_subscription");
|
||||
}
|
||||
|
||||
function onConnectionLost(responseObject) {
|
||||
if (responseObject.errorCode !== 0) {
|
||||
console.log("onConnectionLost:" + responseObject.errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
function onMessageArrived(message) {
|
||||
console.log("onMessageArrived:" + message.payloadString);
|
||||
}
|
||||
function buttonClick() {
|
||||
message = new Paho.MQTT.Message("Hello");
|
||||
message.destinationName = "pahoJS_topic";
|
||||
mqttClient.send(message);
|
||||
}
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<button onclick="buttonClick()">Publish</button>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
{
|
||||
"Kestrel": {
|
||||
"EndPoints": {
|
||||
"Http": {
|
||||
"Url": "http://*:80"
|
||||
},
|
||||
"Https": {
|
||||
"Url": "http://*:443"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MQTT": {
|
||||
/*
|
||||
Wildcard Addresses:
|
||||
* - All local IP addresses
|
||||
localhost - Localhost only
|
||||
disable - Skip address assignment
|
||||
*/
|
||||
"TcpEndPoint": {
|
||||
"Enabled": true,
|
||||
"IPv4": "*",
|
||||
"IPv6": "*",
|
||||
"Port": 1883
|
||||
},
|
||||
"EncryptedTcpEndPoint": {
|
||||
"Enabled": false,
|
||||
"IPv4": "*",
|
||||
"IPv6": "*",
|
||||
"Port": 8883,
|
||||
"Certificate": {
|
||||
"Path": "/absolute/path/to/pfx",
|
||||
"Password": ""
|
||||
}
|
||||
},
|
||||
"WebSocketEndPoint": {
|
||||
"Enabled": true,
|
||||
"Path": "/mqtt",
|
||||
"KeepAliveInterval": 120, // In seconds.
|
||||
"ReceiveBufferSize": 4096,
|
||||
"AllowedOrigins": [] // List of strings with URLs.
|
||||
},
|
||||
"CommunicationTimeout": 15, // In seconds.
|
||||
"ConnectionBacklog": 10, // Set 0 to disable
|
||||
"EnablePersistentSessions": true,
|
||||
"MaxPendingMessagesPerClient": 250,
|
||||
"RetainedApplicationMessages": {
|
||||
"Persist": true,
|
||||
"Path": "RetainedApplicationMessages.json",
|
||||
"WriteInterval": 10 // In seconds.
|
||||
},
|
||||
"EnableDebugLogging": false
|
||||
},
|
||||
"Scripting": {
|
||||
"ScriptsPath": "Scripts",
|
||||
"IncludePaths": []
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
@@ -21,17 +21,15 @@ namespace MQTTnet.Adapter
|
||||
|
||||
long BytesReceived { get; }
|
||||
|
||||
Action ReadingPacketStartedCallback { get; set; }
|
||||
|
||||
Action ReadingPacketCompletedCallback { get; set; }
|
||||
bool IsReadingPacket { get; }
|
||||
|
||||
Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken);
|
||||
|
||||
Task DisconnectAsync(TimeSpan timeout, CancellationToken cancellationToken);
|
||||
|
||||
Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken);
|
||||
Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken);
|
||||
|
||||
Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken);
|
||||
Task<MqttBasePacket> ReceivePacketAsync(CancellationToken cancellationToken);
|
||||
|
||||
void ResetStatistics();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
using MQTTnet.Diagnostics.PacketInspection;
|
||||
|
||||
namespace MQTTnet.Adapter
|
||||
{
|
||||
@@ -19,27 +21,27 @@ namespace MQTTnet.Adapter
|
||||
const uint ErrorOperationAborted = 0x800703E3;
|
||||
const int ReadBufferSize = 4096;
|
||||
|
||||
readonly IMqttNetScopedLogger _logger;
|
||||
readonly IMqttChannel _channel;
|
||||
readonly MqttPacketReader _packetReader;
|
||||
|
||||
readonly byte[] _singleByteBuffer = new byte[1];
|
||||
readonly byte[] _fixedHeaderBuffer = new byte[2];
|
||||
|
||||
readonly MqttPacketInspectorHandler _packetInspectorHandler;
|
||||
readonly MqttNetSourceLogger _logger;
|
||||
readonly IMqttChannel _channel;
|
||||
|
||||
readonly AsyncLock _syncRoot = new AsyncLock();
|
||||
|
||||
long _bytesReceived;
|
||||
long _bytesSent;
|
||||
|
||||
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, IMqttNetLogger logger)
|
||||
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, IMqttPacketInspector packetInspector, IMqttNetLogger logger)
|
||||
{
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
|
||||
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
|
||||
|
||||
_packetReader = new MqttPacketReader(_channel);
|
||||
_packetInspectorHandler = new MqttPacketInspectorHandler(packetInspector, logger);
|
||||
|
||||
_logger = logger.CreateScopedLogger(nameof(MqttChannelAdapter));
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
_logger = logger.WithSource(nameof(MqttChannelAdapter));
|
||||
}
|
||||
|
||||
public string Endpoint => _channel.Endpoint;
|
||||
@@ -51,10 +53,10 @@ namespace MQTTnet.Adapter
|
||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||
|
||||
public long BytesSent => Interlocked.Read(ref _bytesSent);
|
||||
|
||||
public long BytesReceived => Interlocked.Read(ref _bytesReceived);
|
||||
|
||||
public Action ReadingPacketStartedCallback { get; set; }
|
||||
public Action ReadingPacketCompletedCallback { get; set; }
|
||||
public bool IsReadingPacket { get; private set; }
|
||||
|
||||
public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -79,7 +81,7 @@ namespace MQTTnet.Adapter
|
||||
throw;
|
||||
}
|
||||
|
||||
WrapException(exception);
|
||||
WrapAndThrowException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,97 +109,78 @@ namespace MQTTnet.Adapter
|
||||
throw;
|
||||
}
|
||||
|
||||
WrapException(exception);
|
||||
WrapAndThrowException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken)
|
||||
public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
|
||||
using (await _syncRoot.WaitAsync(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
// Check for cancellation here again because "WaitAsync" might take some time.
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
var packetData = PacketFormatterAdapter.Encode(packet);
|
||||
_packetInspectorHandler.BeginSendPacket(packetData);
|
||||
|
||||
await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
Interlocked.Add(ref _bytesReceived, packetData.Count);
|
||||
|
||||
_logger.Verbose("TX ({0} bytes) >>> {1}", packetData.Count, packet);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (IsWrappedException(exception))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
WrapAndThrowException(exception);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PacketFormatterAdapter.FreeBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<MqttBasePacket> ReceivePacketAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
|
||||
try
|
||||
{
|
||||
using (await _syncRoot.WaitAsync(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
// Check for cancellation here again because "WaitAsync" might take some time.
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
_packetInspectorHandler.BeginReceivePacket();
|
||||
|
||||
try
|
||||
{
|
||||
var packetData = PacketFormatterAdapter.Encode(packet);
|
||||
|
||||
if (timeout == TimeSpan.Zero)
|
||||
{
|
||||
await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await MqttTaskTimeout.WaitAsync(
|
||||
t => _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, t), timeout, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
Interlocked.Add(ref _bytesReceived, packetData.Count);
|
||||
|
||||
_logger.Verbose("TX ({0} bytes) >>> {1}", packetData.Count, packet);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (IsWrappedException(exception))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
WrapException(exception);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PacketFormatterAdapter.FreeBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
|
||||
try
|
||||
{
|
||||
ReceivedMqttPacket receivedMqttPacket;
|
||||
if (timeout == TimeSpan.Zero)
|
||||
{
|
||||
receivedMqttPacket = await ReceiveAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
receivedMqttPacket = await MqttTaskTimeout.WaitAsync(ReceiveAsync, timeout, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (receivedMqttPacket == null || cancellationToken.IsCancellationRequested)
|
||||
var receivedPacket = await ReceiveAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (receivedPacket == null || cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Interlocked.Add(ref _bytesSent, receivedMqttPacket.TotalLength);
|
||||
_packetInspectorHandler.EndReceivePacket();
|
||||
|
||||
Interlocked.Add(ref _bytesSent, receivedPacket.TotalLength);
|
||||
|
||||
if (PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.Unknown)
|
||||
{
|
||||
PacketFormatterAdapter.DetectProtocolVersion(receivedMqttPacket);
|
||||
PacketFormatterAdapter.DetectProtocolVersion(receivedPacket);
|
||||
}
|
||||
|
||||
var packet = PacketFormatterAdapter.Decode(receivedMqttPacket);
|
||||
var packet = PacketFormatterAdapter.Decode(receivedPacket);
|
||||
if (packet == null)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Received malformed packet.");
|
||||
}
|
||||
|
||||
_logger.Verbose("RX ({0} bytes) <<< {1}", receivedMqttPacket.TotalLength, packet);
|
||||
_logger.Verbose("RX ({0} bytes) <<< {1}", receivedPacket.TotalLength, packet);
|
||||
|
||||
return packet;
|
||||
}
|
||||
@@ -214,7 +197,7 @@ namespace MQTTnet.Adapter
|
||||
throw;
|
||||
}
|
||||
|
||||
WrapException(exception);
|
||||
WrapAndThrowException(exception);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -239,31 +222,38 @@ namespace MQTTnet.Adapter
|
||||
|
||||
async Task<ReceivedMqttPacket> ReceiveAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var readFixedHeaderResult = await _packetReader.ReadFixedHeaderAsync(_fixedHeaderBuffer, cancellationToken).ConfigureAwait(false);
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var readFixedHeaderResult = await ReadFixedHeaderAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (readFixedHeaderResult.ConnectionClosed)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (readFixedHeaderResult.ConnectionClosed)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ReadingPacketStartedCallback?.Invoke();
|
||||
IsReadingPacket = true;
|
||||
|
||||
var fixedHeader = readFixedHeaderResult.FixedHeader;
|
||||
if (fixedHeader.RemainingLength == 0)
|
||||
{
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, null, 2);
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, new MqttPacketBodyReader(new byte[0], 0, 0), 2);
|
||||
}
|
||||
|
||||
var body = new byte[fixedHeader.RemainingLength];
|
||||
var bodyLength = fixedHeader.RemainingLength;
|
||||
var body = new byte[bodyLength];
|
||||
|
||||
var bodyOffset = 0;
|
||||
var chunkSize = Math.Min(ReadBufferSize, fixedHeader.RemainingLength);
|
||||
var chunkSize = Math.Min(ReadBufferSize, bodyLength);
|
||||
|
||||
do
|
||||
{
|
||||
@@ -286,25 +276,126 @@ namespace MQTTnet.Adapter
|
||||
}
|
||||
|
||||
bodyOffset += readBytes;
|
||||
} while (bodyOffset < body.Length);
|
||||
} while (bodyOffset < bodyLength);
|
||||
|
||||
var bodyReader = new MqttPacketBodyReader(body, 0, body.Length);
|
||||
_packetInspectorHandler.FillReceiveBuffer(body);
|
||||
|
||||
var bodyReader = new MqttPacketBodyReader(body, 0, bodyLength);
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, bodyReader, fixedHeader.TotalLength);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ReadingPacketCompletedCallback?.Invoke();
|
||||
IsReadingPacket = false;
|
||||
}
|
||||
}
|
||||
|
||||
async Task<ReadFixedHeaderResult> ReadFixedHeaderAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
// The MQTT fixed header contains 1 byte of flags and at least 1 byte for the remaining data length.
|
||||
// So in all cases at least 2 bytes must be read for a complete MQTT packet.
|
||||
var buffer = _fixedHeaderBuffer;
|
||||
var totalBytesRead = 0;
|
||||
|
||||
while (totalBytesRead < buffer.Length)
|
||||
{
|
||||
var bytesRead = await _channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (bytesRead == 0)
|
||||
{
|
||||
return new ReadFixedHeaderResult
|
||||
{
|
||||
ConnectionClosed = true
|
||||
};
|
||||
}
|
||||
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
|
||||
_packetInspectorHandler.FillReceiveBuffer(buffer);
|
||||
|
||||
var hasRemainingLength = buffer[1] != 0;
|
||||
if (!hasRemainingLength)
|
||||
{
|
||||
return new ReadFixedHeaderResult
|
||||
{
|
||||
FixedHeader = new MqttFixedHeader(buffer[0], 0, totalBytesRead)
|
||||
};
|
||||
}
|
||||
|
||||
var bodyLength = await ReadBodyLengthAsync(buffer[1], cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (!bodyLength.HasValue)
|
||||
{
|
||||
return new ReadFixedHeaderResult
|
||||
{
|
||||
ConnectionClosed = true
|
||||
};
|
||||
}
|
||||
|
||||
totalBytesRead += bodyLength.Value;
|
||||
return new ReadFixedHeaderResult
|
||||
{
|
||||
FixedHeader = new MqttFixedHeader(buffer[0], bodyLength.Value, totalBytesRead)
|
||||
};
|
||||
}
|
||||
|
||||
async Task<int?> ReadBodyLengthAsync(byte initialEncodedByte, CancellationToken cancellationToken)
|
||||
{
|
||||
var offset = 0;
|
||||
var multiplier = 128;
|
||||
var value = initialEncodedByte & 127;
|
||||
int encodedByte = initialEncodedByte;
|
||||
|
||||
while ((encodedByte & 128) != 0)
|
||||
{
|
||||
offset++;
|
||||
if (offset > 3)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Remaining length is invalid.");
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var readCount = await _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (readCount == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
_packetInspectorHandler.FillReceiveBuffer(_singleByteBuffer);
|
||||
|
||||
encodedByte = _singleByteBuffer[0];
|
||||
|
||||
value += (encodedByte & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool IsWrappedException(Exception exception)
|
||||
{
|
||||
return exception is OperationCanceledException ||
|
||||
exception is MqttCommunicationTimedOutException ||
|
||||
exception is MqttCommunicationException;
|
||||
exception is MqttCommunicationException ||
|
||||
exception is MqttProtocolViolationException;
|
||||
}
|
||||
|
||||
static void WrapException(Exception exception)
|
||||
static void WrapAndThrowException(Exception exception)
|
||||
{
|
||||
if (exception is IOException && exception.InnerException is SocketException innerException)
|
||||
{
|
||||
@@ -313,11 +404,15 @@ namespace MQTTnet.Adapter
|
||||
|
||||
if (exception is SocketException socketException)
|
||||
{
|
||||
if (socketException.SocketErrorCode == SocketError.ConnectionAborted ||
|
||||
socketException.SocketErrorCode == SocketError.OperationAborted)
|
||||
if (socketException.SocketErrorCode == SocketError.OperationAborted)
|
||||
{
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
|
||||
if (socketException.SocketErrorCode == SocketError.ConnectionAborted)
|
||||
{
|
||||
throw new MqttCommunicationException(socketException);
|
||||
}
|
||||
}
|
||||
|
||||
if (exception is COMException comException)
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
using MQTTnet.Client.Connecting;
|
||||
using System;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Exceptions;
|
||||
|
||||
namespace MQTTnet.Adapter
|
||||
{
|
||||
public class MqttConnectingFailedException : MqttCommunicationException
|
||||
public sealed class MqttConnectingFailedException : MqttCommunicationException
|
||||
{
|
||||
public MqttConnectingFailedException(MqttClientAuthenticateResult result)
|
||||
: base($"Connecting with MQTT server failed ({result.ResultCode.ToString()}).")
|
||||
public MqttConnectingFailedException(string message, Exception innerException, MqttClientConnectResult connectResult)
|
||||
: base(message, innerException)
|
||||
{
|
||||
Result = result;
|
||||
Result = connectResult;
|
||||
}
|
||||
|
||||
public MqttClientAuthenticateResult Result { get; }
|
||||
public MqttClientConnectResultCode ResultCode => Result.ResultCode;
|
||||
public MqttClientConnectResult Result { get; }
|
||||
|
||||
public MqttClientConnectResultCode ResultCode => Result?.ResultCode ?? MqttClientConnectResultCode.UnspecifiedError;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
using MQTTnet.Diagnostics.PacketInspection;
|
||||
|
||||
namespace MQTTnet.Adapter
|
||||
{
|
||||
public sealed class MqttPacketInspectorHandler
|
||||
{
|
||||
readonly MemoryStream _receivedPacketBuffer;
|
||||
readonly IMqttPacketInspector _packetInspector;
|
||||
readonly MqttNetSourceLogger _logger;
|
||||
|
||||
public MqttPacketInspectorHandler(IMqttPacketInspector packetInspector, IMqttNetLogger logger)
|
||||
{
|
||||
_packetInspector = packetInspector;
|
||||
|
||||
if (packetInspector != null)
|
||||
{
|
||||
_receivedPacketBuffer = new MemoryStream();
|
||||
}
|
||||
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
_logger = logger.WithSource(nameof(MqttPacketInspectorHandler));
|
||||
}
|
||||
|
||||
public void BeginReceivePacket()
|
||||
{
|
||||
_receivedPacketBuffer?.SetLength(0);
|
||||
}
|
||||
|
||||
public void EndReceivePacket()
|
||||
{
|
||||
if (_packetInspector == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var buffer = _receivedPacketBuffer.ToArray();
|
||||
_receivedPacketBuffer.SetLength(0);
|
||||
|
||||
InspectPacket(buffer, MqttPacketFlowDirection.Inbound);
|
||||
}
|
||||
|
||||
public void BeginSendPacket(ArraySegment<byte> buffer)
|
||||
{
|
||||
if (_packetInspector == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a copy of the actual packet so that the inspector gets no access
|
||||
// to the internal buffers. This is waste of memory but this feature is only
|
||||
// intended for debugging etc. so that this is OK.
|
||||
var bufferCopy = buffer.ToArray();
|
||||
|
||||
InspectPacket(bufferCopy, MqttPacketFlowDirection.Outbound);
|
||||
}
|
||||
|
||||
public void FillReceiveBuffer(byte[] buffer)
|
||||
{
|
||||
_receivedPacketBuffer?.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
void InspectPacket(byte[] buffer, MqttPacketFlowDirection direction)
|
||||
{
|
||||
try
|
||||
{
|
||||
var context = new ProcessMqttPacketContext
|
||||
{
|
||||
Buffer = buffer,
|
||||
Direction = direction
|
||||
};
|
||||
|
||||
_packetInspector.ProcessMqttPacket(context);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error(exception, "Error while inspecting packet.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,20 @@
|
||||
using MQTTnet.Formatter;
|
||||
using System;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Adapter
|
||||
{
|
||||
public class ReceivedMqttPacket
|
||||
public sealed class ReceivedMqttPacket
|
||||
{
|
||||
public ReceivedMqttPacket(byte fixedHeader, IMqttPacketBodyReader body, int totalLength)
|
||||
public ReceivedMqttPacket(byte fixedHeader, IMqttPacketBodyReader bodyReader, int totalLength)
|
||||
{
|
||||
FixedHeader = fixedHeader;
|
||||
Body = body;
|
||||
BodyReader = bodyReader ?? throw new ArgumentNullException(nameof(bodyReader));
|
||||
TotalLength = totalLength;
|
||||
}
|
||||
|
||||
public byte FixedHeader { get; set; }
|
||||
public byte FixedHeader { get; }
|
||||
|
||||
public IMqttPacketBodyReader Body { get; }
|
||||
public IMqttPacketBodyReader BodyReader { get; }
|
||||
|
||||
public int TotalLength { get; }
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Client.Connecting
|
||||
{
|
||||
// TODO: Consider renaming this to _MqttClientConnectResult_
|
||||
public class MqttClientAuthenticateResult
|
||||
{
|
||||
public MqttClientConnectResultCode ResultCode { get; set; }
|
||||
|
||||
public bool IsSessionPresent { get; set; }
|
||||
|
||||
public bool? WildcardSubscriptionAvailable { get; set; }
|
||||
|
||||
public bool? RetainAvailable { get; set; }
|
||||
|
||||
public string AssignedClientIdentifier { get; set; }
|
||||
|
||||
public string AuthenticationMethod { get; set; }
|
||||
|
||||
public byte[] AuthenticationData { get; set; }
|
||||
|
||||
public uint? MaximumPacketSize { get; set; }
|
||||
|
||||
public string ReasonString { get; set; }
|
||||
|
||||
public ushort? ReceiveMaximum { get; set; }
|
||||
|
||||
public string ResponseInformation { get; set; }
|
||||
|
||||
public ushort? TopicAliasMaximum { get; set; }
|
||||
|
||||
public string ServerReference { get; set; }
|
||||
|
||||
public ushort? ServerKeepAlive { get; set; }
|
||||
|
||||
public uint? SessionExpiryInterval { get; set; }
|
||||
|
||||
public bool? SubscriptionIdentifiersAvailable { get; set; }
|
||||
|
||||
public bool? SharedSubscriptionAvailable { get; set; }
|
||||
|
||||
public List<MqttUserProperty> UserProperties { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Client.Connecting
|
||||
{
|
||||
public sealed class MqttClientConnectResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the result code.
|
||||
/// MQTTv5 only.
|
||||
/// </summary>
|
||||
public MqttClientConnectResultCode ResultCode { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a session was already available or not.
|
||||
/// MQTTv5 only.
|
||||
/// </summary>
|
||||
public bool IsSessionPresent { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether wildcards can be used in subscriptions at the current server.
|
||||
/// MQTTv5 only.
|
||||
/// </summary>
|
||||
public bool WildcardSubscriptionAvailable { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the server supports retained messages.
|
||||
/// MQTTv5 only.
|
||||
/// </summary>
|
||||
public bool RetainAvailable { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the client identifier which was chosen by the server.
|
||||
/// MQTTv5 only.
|
||||
/// </summary>
|
||||
public string AssignedClientIdentifier { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authentication method.
|
||||
/// MQTTv5 only.
|
||||
/// </summary>
|
||||
public string AuthenticationMethod { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authentication data.
|
||||
/// MQTTv5 only.
|
||||
/// </summary>
|
||||
public byte[] AuthenticationData { get; internal set; }
|
||||
|
||||
public uint? MaximumPacketSize { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reason string.
|
||||
/// MQTTv5 only.
|
||||
/// </summary>
|
||||
public string ReasonString { get; internal set; }
|
||||
|
||||
public ushort? ReceiveMaximum { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum QoS which is supported by the server.
|
||||
/// MQTTv5 only.
|
||||
/// </summary>
|
||||
public MqttQualityOfServiceLevel MaximumQoS { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the response information.
|
||||
/// MQTTv5 only.
|
||||
/// </summary>
|
||||
public string ResponseInformation { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum value for a topic alias. 0 means not supported.
|
||||
/// MQTTv5 only.
|
||||
/// </summary>
|
||||
public ushort TopicAliasMaximum { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an alternate server which should be used instead of the current one.
|
||||
/// MQTTv5 only.
|
||||
/// </summary>
|
||||
public string ServerReference { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the keep alive interval which was chosen by the server instead of the
|
||||
/// keep alive interval from the client CONNECT packet.
|
||||
/// MQTTv5 only.
|
||||
/// </summary>
|
||||
public ushort? ServerKeepAlive { get; internal set; }
|
||||
|
||||
public uint? SessionExpiryInterval { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the subscription identifiers are available or not.
|
||||
/// MQTTv5 only.
|
||||
/// </summary>
|
||||
public bool SubscriptionIdentifiersAvailable { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the shared subscriptions are available or not.
|
||||
/// MQTTv5 only.
|
||||
/// </summary>
|
||||
public bool SharedSubscriptionAvailable { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user properties.
|
||||
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
|
||||
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
|
||||
/// The feature is very similar to the HTTP header concept.
|
||||
/// MQTTv5 only.
|
||||
/// </summary>
|
||||
public List<MqttUserProperty> UserProperties { get; internal set; }
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,15 @@ namespace MQTTnet.Client.Connecting
|
||||
{
|
||||
public class MqttClientConnectedEventArgs : EventArgs
|
||||
{
|
||||
public MqttClientConnectedEventArgs(MqttClientAuthenticateResult authenticateResult)
|
||||
public MqttClientConnectedEventArgs(MqttClientConnectResult connectResult)
|
||||
{
|
||||
AuthenticateResult = authenticateResult ?? throw new ArgumentNullException(nameof(authenticateResult));
|
||||
ConnectResult = connectResult ?? throw new ArgumentNullException(nameof(connectResult));
|
||||
}
|
||||
|
||||
public MqttClientAuthenticateResult AuthenticateResult { get; }
|
||||
/// <summary>
|
||||
/// Gets the authentication result.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public MqttClientConnectResult ConnectResult { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
namespace MQTTnet.Client.Disconnecting
|
||||
{
|
||||
public class MqttClientDisconnectOptions
|
||||
public sealed class MqttClientDisconnectOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the reason code.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public MqttClientDisconnectReason ReasonCode { get; set; } = MqttClientDisconnectReason.NormalDisconnection;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the reason string.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public string ReasonString { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
ServerBusy = 137,
|
||||
ServerShuttingDown = 139,
|
||||
BadAuthenticationMethod = 140,
|
||||
KeepaliveTimeout = 141,
|
||||
KeepAliveTimeout = 141,
|
||||
SessionTakenOver = 142,
|
||||
TopicFilterInvalid = 143,
|
||||
TopicNameInvalid = 144,
|
||||
|
||||
@@ -3,22 +3,35 @@ using MQTTnet.Client.Connecting;
|
||||
|
||||
namespace MQTTnet.Client.Disconnecting
|
||||
{
|
||||
public class MqttClientDisconnectedEventArgs : EventArgs
|
||||
public sealed class MqttClientDisconnectedEventArgs : EventArgs
|
||||
{
|
||||
public MqttClientDisconnectedEventArgs(bool clientWasConnected, Exception exception, MqttClientAuthenticateResult authenticateResult, MqttClientDisconnectReason reasonCode)
|
||||
public MqttClientDisconnectedEventArgs(bool clientWasConnected, Exception exception, MqttClientConnectResult connectResult, MqttClientDisconnectReason reason)
|
||||
{
|
||||
ClientWasConnected = clientWasConnected;
|
||||
Exception = exception;
|
||||
AuthenticateResult = authenticateResult;
|
||||
ReasonCode = reasonCode;
|
||||
ConnectResult = connectResult;
|
||||
Reason = reason;
|
||||
|
||||
ReasonCode = reason;
|
||||
}
|
||||
|
||||
public bool ClientWasConnected { get; }
|
||||
|
||||
public Exception Exception { get; }
|
||||
|
||||
public MqttClientAuthenticateResult AuthenticateResult { get; }
|
||||
/// <summary>
|
||||
/// Gets the authentication result.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public MqttClientConnectResult ConnectResult { get; }
|
||||
|
||||
public MqttClientDisconnectReason ReasonCode { get; set; } = MqttClientDisconnectReason.NormalDisconnection;
|
||||
/// <summary>
|
||||
/// Gets or sets the reason.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public MqttClientDisconnectReason Reason { get; set; }
|
||||
|
||||
[Obsolete("Please use 'Reason' instead. This property will be removed in the future!")]
|
||||
public MqttClientDisconnectReason ReasonCode { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user