Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f5d4e9825 | ||
|
|
e80d19ba9b | ||
|
|
48837529a2 | ||
|
|
43e3ad4c00 | ||
|
|
08c9ecb011 | ||
|
|
763f0e7934 | ||
|
|
9d381c2cf9 | ||
|
|
17ceb20169 | ||
|
|
683bb43486 | ||
|
|
d25b51740b | ||
|
|
a58ab607ad | ||
|
|
0a7d450f3b | ||
|
|
999fa7a468 | ||
|
|
f0a1f9ca93 | ||
|
|
89e9cb224f | ||
|
|
fe48c9c837 | ||
|
|
2a421d1d42 | ||
|
|
48d0344a40 | ||
|
|
db5ed594f7 | ||
|
|
f85bc9b956 | ||
|
|
eec46dd9bc | ||
|
|
9c9e2293c8 | ||
|
|
856861e1b1 | ||
|
|
4b481b4833 | ||
|
|
c05f11a495 | ||
|
|
e798c91ff3 | ||
|
|
5df4fc3d5d | ||
|
|
c6236c0865 | ||
|
|
85c0cb3bcc | ||
|
|
9810e52bcb | ||
|
|
948a5eaae3 | ||
|
|
31df8247fa | ||
|
|
229418100c | ||
|
|
93d8679bbc | ||
|
|
6cec126862 | ||
|
|
9749f790ea | ||
|
|
fbbc638c75 | ||
|
|
bf679adb6b | ||
|
|
cf7e59a774 | ||
|
|
e9724c0202 | ||
|
|
7931af7487 | ||
|
|
bb5f023b74 | ||
|
|
c2ac4fffaf | ||
|
|
4b78710705 | ||
|
|
2e69f8be78 | ||
|
|
2d6c44e45b | ||
|
|
f001d7aecc | ||
|
|
d49a4384c0 | ||
|
|
4e6658e014 | ||
|
|
2b2b30912c | ||
|
|
79d95c8516 | ||
|
|
4b08bb41f5 | ||
|
|
c89918a890 | ||
|
|
6546175ea2 | ||
|
|
b9e9fe64ac | ||
|
|
9fd7c7bc1e | ||
|
|
fcd6b7ff52 | ||
|
|
54870adc64 | ||
|
|
892e4842ed | ||
|
|
fc358f9015 | ||
|
|
b93cd5b673 | ||
|
|
aafc99f39e | ||
|
|
b14d7affe5 | ||
|
|
67d56644b2 | ||
|
|
6e0d3ec0fb | ||
|
|
2c6613a7b7 | ||
|
|
d464bfb7c5 | ||
|
|
7a24970035 | ||
|
|
db906eca24 | ||
|
|
ad128c7889 | ||
|
|
3e096bc353 | ||
|
|
f0be8404cf | ||
|
|
6de28d32e4 | ||
|
|
2ede7df38a | ||
|
|
2a35108a73 | ||
|
|
774d49a908 | ||
|
|
c83bec6e81 | ||
|
|
763d9778bb | ||
|
|
9368eebfd5 | ||
|
|
81b547f386 | ||
|
|
e33c5ccb4c | ||
|
|
0ca4c88e69 | ||
|
|
04ccdcd562 | ||
|
|
aafd4ea658 | ||
|
|
4eb65fc3d2 | ||
|
|
f7f00f4e06 | ||
|
|
af0b98eace | ||
|
|
0da2ab9de6 | ||
|
|
ea22ef4164 | ||
|
|
cb5a0cf38b | ||
|
|
55dc940abb | ||
|
|
c40e3c64aa | ||
|
|
87c4fa14ee | ||
|
|
0653b89701 | ||
|
|
561474c572 | ||
|
|
c10145f743 | ||
|
|
dca2af47d8 | ||
|
|
9ed80daf83 | ||
|
|
0d39bbbc0c | ||
|
|
52faf6d76a | ||
|
|
20eda96921 | ||
|
|
f6a814c6d7 | ||
|
|
8e0bce7734 | ||
|
|
72fa13ca5b | ||
|
|
45e3455e6c | ||
|
|
160da00f55 | ||
|
|
6b4b829cf6 | ||
|
|
e7fa465616 | ||
|
|
799005803e | ||
|
|
a20c845a5a | ||
|
|
5f59aca62c | ||
|
|
76ddf8c503 | ||
|
|
122034296c | ||
|
|
6f71aaf1ce | ||
|
|
fd8caed9af | ||
|
|
313f90a526 | ||
|
|
4d27f42e72 | ||
|
|
8ae318fd5c | ||
|
|
7dde2f1f4c | ||
|
|
49c5278ba0 | ||
|
|
ce51d12a03 | ||
|
|
b1559a8fa5 | ||
|
|
457143c840 | ||
|
|
c60bf51b1e | ||
|
|
630400da53 | ||
|
|
a152066ed3 | ||
|
|
138cd16c8d | ||
|
|
27a61819d3 | ||
|
|
9c378fb088 | ||
|
|
d4088420cf | ||
|
|
2e7d074174 | ||
|
|
328ffc734f | ||
|
|
8ecf658059 | ||
|
|
7b25c6f274 | ||
|
|
069c19aab9 | ||
|
|
35e729bd7b | ||
|
|
ac06a001e2 | ||
|
|
a77e0b8dde | ||
|
|
698f0b66d2 | ||
|
|
14419e4d63 | ||
|
|
3600ebbc62 | ||
|
|
55676965d9 | ||
|
|
f949d54aef | ||
|
|
624fafc662 | ||
|
|
23e93212a4 | ||
|
|
f9b2c91ddb | ||
|
|
9fb1cc3320 | ||
|
|
1c89a8bc75 | ||
|
|
8be9ce8024 | ||
|
|
002ba27fd3 | ||
|
|
9aaa94e821 | ||
|
|
e8ff48bfd3 | ||
|
|
5db05f186f | ||
|
|
b2c2209b4f | ||
|
|
c490e83c91 | ||
|
|
ac326babdf | ||
|
|
a38623cb5a | ||
|
|
a63489fa5e | ||
|
|
4b7409240b | ||
|
|
896e9ed0aa | ||
|
|
0950dfbb8b | ||
|
|
4105beb7e6 | ||
|
|
5c25905510 | ||
|
|
e3f6b579b2 | ||
|
|
c467c9c818 | ||
|
|
78b57531b6 | ||
|
|
84d9f533bd | ||
|
|
7479e320ee | ||
|
|
152303dfbd | ||
|
|
a495668f2b | ||
|
|
82b714d4c7 | ||
|
|
44d71b884c | ||
|
|
781c5d4d7d | ||
|
|
b175119008 | ||
|
|
cfcb858423 | ||
|
|
5fa12649bf | ||
|
|
a9a09f4ecd | ||
|
|
3ff5806dbb | ||
|
|
0ec78cf5cc | ||
|
|
c3193c568a | ||
|
|
a1f02284e9 | ||
|
|
851db2e808 | ||
|
|
073cd5243f | ||
|
|
699558e47a | ||
|
|
15ff03c136 | ||
|
|
b12e3bc611 | ||
|
|
61c3e02242 | ||
|
|
5618c4c1f7 | ||
|
|
ba9ceed7ce | ||
|
|
ad1c198e43 | ||
|
|
2918be18c6 | ||
|
|
248e2a5bdd | ||
|
|
1a41415dd1 | ||
|
|
9302e80d73 | ||
|
|
cf6f0ece0e | ||
|
|
50d65f2fed | ||
|
|
4614df7fa5 | ||
|
|
22aedcba09 | ||
|
|
2767d897a4 | ||
|
|
a4420063c0 | ||
|
|
2320352124 | ||
|
|
c0b4155ab4 | ||
|
|
b09f31d654 | ||
|
|
30c33d46f9 | ||
|
|
651b79a124 | ||
|
|
2e3cf54f11 | ||
|
|
d7948afd98 | ||
|
|
4cefa1dfa9 | ||
|
|
0cfb8b4020 | ||
|
|
9ac726b271 | ||
|
|
232605304e | ||
|
|
6c53677e7d | ||
|
|
6d0e2c2022 | ||
|
|
8ad1fb2871 | ||
|
|
4a7ff9e9ed | ||
|
|
f54598c201 | ||
|
|
2300e68fff | ||
|
|
6996824157 | ||
|
|
524f695829 | ||
|
|
72faa067cc | ||
|
|
01aa75471c | ||
|
|
048206eb1e | ||
|
|
ad5b0caa4d | ||
|
|
0f14ddcf79 | ||
|
|
59d2a8e551 | ||
|
|
8607ceb3b7 | ||
|
|
b82e5a48a4 | ||
|
|
30c5147c62 | ||
|
|
9d63500f95 | ||
|
|
a03bdc7616 | ||
|
|
413daee551 | ||
|
|
f3ea7008e5 | ||
|
|
8bfbbd2b29 | ||
|
|
ab8c7df0b9 | ||
|
|
975eb60e90 | ||
|
|
6076151222 | ||
|
|
9cc96f1888 | ||
|
|
7aa4d5159c | ||
|
|
9b021c4093 | ||
|
|
54c303369e | ||
|
|
8f30042e49 | ||
|
|
4c33270d26 | ||
|
|
5c40577bec | ||
|
|
3d404f9755 | ||
|
|
1b5e0197e4 | ||
|
|
1dab729d84 | ||
|
|
3eac5bcaf3 | ||
|
|
8eb23f0f1d | ||
|
|
1fd1a12fce | ||
|
|
349d506b37 | ||
|
|
a981b05aa7 | ||
|
|
654f06d20d | ||
|
|
c92cd10499 | ||
|
|
ed06ea6bc9 | ||
|
|
3d531151c6 | ||
|
|
e1590fc0c8 | ||
|
|
dbb227406b | ||
|
|
b891e1d1f4 | ||
|
|
1d697e3b87 | ||
|
|
128484c55a | ||
|
|
ff22c34bb2 | ||
|
|
e7ba3f76dc | ||
|
|
1ae3324581 | ||
|
|
ba8ce15ab4 | ||
|
|
87b4ce5185 | ||
|
|
4eb2b77138 | ||
|
|
70ee49f3c6 | ||
|
|
f223a26748 | ||
|
|
4574fdc951 | ||
|
|
4f66614cdd | ||
|
|
6608172719 | ||
|
|
6c8db47e25 | ||
|
|
3eb5e82d10 | ||
|
|
8563230f9c | ||
|
|
019f428f0a | ||
|
|
1847af13d2 | ||
|
|
8916df8d7b | ||
|
|
f66da92299 | ||
|
|
979df5f17e | ||
|
|
d50c8153b2 | ||
|
|
b3ab7a1069 | ||
|
|
6af58ad90d | ||
|
|
5c36a2d402 | ||
|
|
6df70cc5ed | ||
|
|
d5b0893e90 | ||
|
|
2ed8e55394 | ||
|
|
c5b6ca204d | ||
|
|
7a767c7d2f | ||
|
|
dd96cf98a8 | ||
|
|
6ee3d1640f | ||
|
|
6f8272210c | ||
|
|
ce05cc9936 | ||
|
|
985c3084a8 | ||
|
|
7b3be3a64f | ||
|
|
c50fb740fc | ||
|
|
5895a73f85 | ||
|
|
961a497798 | ||
|
|
6a90864c44 | ||
|
|
a9017b8795 | ||
|
|
f76e130283 | ||
|
|
5a48b146b4 | ||
|
|
1d1def3275 | ||
|
|
bd75a2440e | ||
|
|
7a642802ed | ||
|
|
a8f8601bc5 | ||
|
|
0c7a5a992b | ||
|
|
bd665c3d56 | ||
|
|
7e7b36aadf | ||
|
|
a79c2484b3 | ||
|
|
e4e46f0094 | ||
|
|
5a4c735b85 | ||
|
|
42516942ac | ||
|
|
591ccfec29 | ||
|
|
ffc1d950da | ||
|
|
1c004ead6d | ||
|
|
b0ad6bda70 | ||
|
|
6262a16504 | ||
|
|
0de683979d | ||
|
|
0a6f564446 | ||
|
|
f0be3ef0cf | ||
|
|
f79eebdf64 | ||
|
|
c3c0dcf313 | ||
|
|
60c6074953 | ||
|
|
2b9b22def6 | ||
|
|
5cfb092449 | ||
|
|
a450fc4e01 | ||
|
|
ffe9c57ae8 | ||
|
|
0257e31910 | ||
|
|
7fe5f521dc | ||
|
|
2d03c267e4 | ||
|
|
ccfe67e6a6 | ||
|
|
98e17903ea | ||
|
|
c7d56a78f3 | ||
|
|
b491c74228 | ||
|
|
a2414f48ca | ||
|
|
396e0e1697 | ||
|
|
01f6c959e1 | ||
|
|
f425d87562 | ||
|
|
6614b23ab8 | ||
|
|
02c9133a1c | ||
|
|
913afa3977 | ||
|
|
e068286f15 | ||
|
|
373fe8c72c | ||
|
|
396f040aae | ||
|
|
c90b5f6fb2 | ||
|
|
744b1b302b | ||
|
|
78e504dc2c | ||
|
|
79518d4a35 | ||
|
|
a50fb2a320 | ||
|
|
9fa9d22097 | ||
|
|
e4351b73bd | ||
|
|
2d3fdd4faf | ||
|
|
0a509fd685 | ||
|
|
d1a17314ef | ||
|
|
e96b66e9f8 | ||
|
|
80b05a8944 | ||
|
|
ab3df2ba65 | ||
|
|
f169aaa79b | ||
|
|
231001850a | ||
|
|
6839970d41 | ||
|
|
4df29be155 | ||
|
|
c5cfede231 | ||
|
|
c22b6033e3 | ||
|
|
27476a9194 | ||
|
|
12973a4179 | ||
|
|
01b437dc08 | ||
|
|
ac88f0a05a | ||
|
|
b02f30844c | ||
|
|
1a8bcae47c | ||
|
|
664549ab93 | ||
|
|
914d2b641f | ||
|
|
0cc0fcf5d9 | ||
|
|
5980cbd944 | ||
|
|
3ef7f60a18 | ||
|
|
efef23e3f5 | ||
|
|
53ab2ac140 | ||
|
|
2ecbd83d06 | ||
|
|
a278b6189b | ||
|
|
cdbdfdec6d | ||
|
|
808c2da5f6 | ||
|
|
6ed3b0cedd | ||
|
|
9da3377aa7 | ||
|
|
0cac2aa1bc | ||
|
|
2d59b5c1ac | ||
|
|
9dfa0b32e4 | ||
|
|
20a7579156 | ||
|
|
c7409c2ff4 | ||
|
|
d4c6fb44c9 | ||
|
|
e6cf23aebd | ||
|
|
0fec860b25 | ||
|
|
2c927c5995 | ||
|
|
6399172582 | ||
|
|
5c903e6fbf | ||
|
|
f60bbf7dd7 | ||
|
|
519bb05aac | ||
|
|
a8434d1b58 | ||
|
|
420b35e37d | ||
|
|
a25c8cd4c7 | ||
|
|
8ac77558af | ||
|
|
953578eba2 | ||
|
|
527713ccf8 | ||
|
|
477e2d23ab | ||
|
|
03c61ad82c | ||
|
|
9475ccd0d8 | ||
|
|
73b051d159 | ||
|
|
a9944def68 | ||
|
|
aee10ba6fa | ||
|
|
cfc1b27950 | ||
|
|
149d01e173 | ||
|
|
888e1797d7 | ||
|
|
f8ade2daf4 | ||
|
|
a85792c392 | ||
|
|
fbca8a6dc1 | ||
|
|
732ea84caa | ||
|
|
65896269da | ||
|
|
631f660d5c | ||
|
|
7f08371532 | ||
|
|
1afd59325f | ||
|
|
45c033f401 | ||
|
|
7855eccb78 | ||
|
|
d51300d32d | ||
|
|
61b10a8b56 | ||
|
|
dbee941dcc | ||
|
|
fd7c98d0d1 | ||
|
|
c911797f1b | ||
|
|
b5a1d3a69f | ||
|
|
cdeb073bb0 | ||
|
|
a8db0dd3d6 | ||
|
|
2267770a72 | ||
|
|
35ee732045 | ||
|
|
ad5834dd1f | ||
|
|
a7204234f6 | ||
|
|
e8c33077b7 | ||
|
|
bd86dae8a9 | ||
|
|
7188aaf79d | ||
|
|
bafcc81672 | ||
|
|
d0f1659eec | ||
|
|
11a5b5a080 | ||
|
|
7ed5580352 | ||
|
|
efc6f124b4 | ||
|
|
7c3d88f62d | ||
|
|
b383a54416 | ||
|
|
673fc5e20c | ||
|
|
f2b9642712 | ||
|
|
4f03f22afb | ||
|
|
27a4af6070 | ||
|
|
0184ce400c | ||
|
|
653ea8a102 | ||
|
|
3450889e84 | ||
|
|
1311535eed | ||
|
|
e1799ea6a3 | ||
|
|
cd76f434e3 | ||
|
|
fd91170aee | ||
|
|
cd1bfc3a10 | ||
|
|
f074e9ed04 | ||
|
|
562a5304f6 | ||
|
|
1e697a94a1 | ||
|
|
0e2c3abd05 | ||
|
|
7321774c45 | ||
|
|
4a65cab769 | ||
|
|
510c3bd0bf | ||
|
|
0c056b511e | ||
|
|
2ca6866c5e | ||
|
|
ebcce9b8be | ||
|
|
9e7c70e649 | ||
|
|
bef0860e53 | ||
|
|
67c9177731 | ||
|
|
007991f073 | ||
|
|
532dddeb6c | ||
|
|
9070087fad | ||
|
|
de8bcb7f37 | ||
|
|
5090cfb411 | ||
|
|
6e64d653c5 | ||
|
|
461bc6e594 | ||
|
|
0d95dc563c | ||
|
|
03bfc5f00b | ||
|
|
8708609498 | ||
|
|
dffc27c1f4 | ||
|
|
6f5f507323 | ||
|
|
26d6d817cd | ||
|
|
b68247f56d | ||
|
|
3b02a5f6aa | ||
|
|
09718a00e7 | ||
|
|
ecc2cd5f0c | ||
|
|
618580dc15 | ||
|
|
4860cec6c7 | ||
|
|
b9e0dbf150 | ||
|
|
2a4eb460da | ||
|
|
087c622122 | ||
|
|
2000de15e7 | ||
|
|
5e6b902430 | ||
|
|
a1f414a06d | ||
|
|
de3a39bdc2 | ||
|
|
6c5c9e5bf2 | ||
|
|
506873ed5b | ||
|
|
6a2bded181 | ||
|
|
ebf0e91ab8 | ||
|
|
107e96ff7d | ||
|
|
0d6e244f84 | ||
|
|
96a67579e2 | ||
|
|
4cba814716 | ||
|
|
b527059b72 | ||
|
|
c0507fcc55 | ||
|
|
4fed80c2d0 |
@@ -0,0 +1,5 @@
|
||||
[*.cs]
|
||||
|
||||
|
||||
# CA1031: Do not catch general exception types
|
||||
dotnet_diagnostic.CA1031.severity = none
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve.
|
||||
title: ''
|
||||
labels: 'bug'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### Describe the bug
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
### Which project is your bug related to?
|
||||
<!-- Remove the items which don't apply from the following list -->
|
||||
- Client
|
||||
- ManagedClient
|
||||
- MQTTnet.Server standalone
|
||||
- Server
|
||||
- Generic
|
||||
|
||||
### To Reproduce
|
||||
Steps to reproduce the behavior:
|
||||
1. Using this version of MQTTnet '...'.
|
||||
2. Run this code '....'.
|
||||
3. With these arguments '....'.
|
||||
4. See error.
|
||||
|
||||
### Expected behavior
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
### Screenshots
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
### Additional context / logging
|
||||
Add any other context about the problem here.
|
||||
Include debugging or logging information here:
|
||||
|
||||
```batch
|
||||
\\ Put your logging output here.
|
||||
```
|
||||
### Code example
|
||||
Please provide full code examples below where possible to make it easier for the developers to check your issues.
|
||||
|
||||
```csharp
|
||||
\\ Put your code here.
|
||||
```
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
name: Custom issue template
|
||||
about: Do you have a question related to the project? Use this template.
|
||||
title: ''
|
||||
labels: 'question'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### Describe your question
|
||||
A clear and concise description of what you want to know.
|
||||
|
||||
### Which project is your question related to?
|
||||
<!-- Remove the items which don't apply from the following list -->
|
||||
- Client
|
||||
- ManagedClient
|
||||
- MQTTnet.Server standalone
|
||||
- Server
|
||||
- Generic
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project.
|
||||
title: ''
|
||||
labels: 'feature-request'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### Describe the feature request
|
||||
Is your feature request related to a problem? Please describe.
|
||||
A clear and concise description of what the problem is. Example. I'm am trying to do [...] but [...]
|
||||
|
||||
### Which project is your feature request related to?
|
||||
<!-- Remove the items which don't apply from the following list -->
|
||||
- Client
|
||||
- ManagedClient
|
||||
- MQTTnet.Server standalone
|
||||
- Server
|
||||
- Generic
|
||||
|
||||
### Describe the solution you'd like
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
### Describe alternatives you've considered
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
### Additional context
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -235,6 +235,7 @@ FakesAssemblies/
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
/*/**/package-lock.json
|
||||
|
||||
# Typescript v1 declaration files
|
||||
typings/
|
||||
@@ -287,9 +288,9 @@ __pycache__/
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
|
||||
Build/nuget.exe
|
||||
*.js
|
||||
*.map
|
||||
|
||||
/Tests/MQTTnet.TestApp.NetCore/RetainedMessages.json
|
||||
|
||||
Build/NuGet/
|
||||
|
||||
@@ -3,27 +3,40 @@
|
||||
<metadata>
|
||||
<id>MQTTnet.AspNetCore</id>
|
||||
<version>0.0.0</version>
|
||||
<authors>Christian Kratky</authors>
|
||||
<authors>The contributors of MQTTnet</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
||||
<license type="file">LICENSE</license>
|
||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<icon>images\Logo_128x128.png</icon>
|
||||
<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-2018</copyright>
|
||||
<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</tags>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
|
||||
<dependency id="Microsoft.AspNetCore.Connections.Abstractions" version="2.1.3" />
|
||||
<dependency id="Microsoft.AspNetCore.WebSockets" version="2.1.1" />
|
||||
<dependency id="Microsoft.Extensions.Hosting.Abstractions" version="2.1.1" />
|
||||
<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>
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- License -->
|
||||
<file src="..\LICENSE" />
|
||||
|
||||
<!-- Images -->
|
||||
<file src="..\Images\Logo_128x128.png" target="images\" />
|
||||
|
||||
<!-- .NET Standard 2.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\"/>
|
||||
</files>
|
||||
</package>
|
||||
|
||||
@@ -3,15 +3,16 @@
|
||||
<metadata>
|
||||
<id>MQTTnet.Extensions.ManagedClient</id>
|
||||
<version>0.0.0</version>
|
||||
<authors>Christian Kratky</authors>
|
||||
<authors>The contributors of MQTTnet</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
||||
<license type="file">LICENSE</license>
|
||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<icon>images\Logo_128x128.png</icon>
|
||||
<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-2018</copyright>
|
||||
<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</tags>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
@@ -19,6 +20,12 @@
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- License -->
|
||||
<file src="..\LICENSE" />
|
||||
|
||||
<!-- Images -->
|
||||
<file src="..\Images\Logo_128x128.png" target="images\" />
|
||||
|
||||
<!-- .NET Standard 1.3 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard1.3\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard1.3\"/>
|
||||
|
||||
@@ -31,10 +38,9 @@
|
||||
<!-- .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\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net461\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net462\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net470\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net471\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net472\"/>
|
||||
<!-- .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\"/>
|
||||
|
||||
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
|
||||
</files>
|
||||
</package>
|
||||
</package>
|
||||
|
||||
@@ -3,15 +3,16 @@
|
||||
<metadata>
|
||||
<id>MQTTnet.Extensions.Rpc</id>
|
||||
<version>0.0.0</version>
|
||||
<authors>Christian Kratky</authors>
|
||||
<authors>The contributors of MQTTnet</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
||||
<license type="file">LICENSE</license>
|
||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<icon>images\Logo_128x128.png</icon>
|
||||
<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-2018</copyright>
|
||||
<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</tags>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
@@ -19,6 +20,12 @@
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- License -->
|
||||
<file src="..\LICENSE" />
|
||||
|
||||
<!-- Images -->
|
||||
<file src="..\Images\Logo_128x128.png" target="images\" />
|
||||
|
||||
<!-- .NET Standard 1.3 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard1.3\MQTTnet.Extensions.Rpc.*" target="lib\netstandard1.3\"/>
|
||||
|
||||
@@ -31,10 +38,9 @@
|
||||
<!-- .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\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net461\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net462\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net470\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net471\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net472\"/>
|
||||
<!-- .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\"/>
|
||||
|
||||
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
|
||||
</files>
|
||||
</package>
|
||||
</package>
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0"?>
|
||||
<package >
|
||||
<metadata>
|
||||
<id>MQTTnet.Extensions.WebSocket4Net</id>
|
||||
<version>0.0.0</version>
|
||||
<authors>The contributors of MQTTnet</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<license type="file">LICENSE</license>
|
||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<icon>images\Logo_128x128.png</icon>
|
||||
<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</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>
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- License -->
|
||||
<file src="..\LICENSE" />
|
||||
|
||||
<!-- Images -->
|
||||
<file src="..\Images\Logo_128x128.png" target="images\" />
|
||||
|
||||
<!-- .NET Standard 1.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\"/>
|
||||
|
||||
<!-- Universal Windows -->
|
||||
<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\"/>
|
||||
|
||||
<!-- .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\"/>
|
||||
|
||||
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
|
||||
</files>
|
||||
</package>
|
||||
@@ -3,21 +3,20 @@
|
||||
<metadata>
|
||||
<id>MQTTnet</id>
|
||||
<version>0.0.0</version>
|
||||
<authors>Christian Kratky</authors>
|
||||
<authors>The contributors of MQTTnet</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
||||
<license type="file">LICENSE</license>
|
||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description>
|
||||
<releaseNotes> * [Core] Added support for MQTTv5 packages.
|
||||
* [Client] Added new MQTTv5 features to options builder.
|
||||
* [Client] Added uniform API across all supported MQTT versions (BREAKING CHANGE!)
|
||||
* [Server] Added support for MQTTv5 clients. The server will still return _success_ for all cases at the moment even if more granular codes are available.
|
||||
|
||||
* [Note] Due to MQTTv5 a lot of new classes were introduced. This required adding new namespaces as well. Most classes are backward compatible but new namespaces must be added.
|
||||
<icon>images\Logo_128x128.png</icon>
|
||||
<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 issue in keep alive handling.
|
||||
* [MQTTnet.Server] Added support for delivering static files (HTML, JavaScript etc.).
|
||||
* [MQTTnet.Server] Fixed web socket protocol errors (when using paho JS etc.).
|
||||
</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
||||
<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</tags>
|
||||
<dependencies>
|
||||
<group targetFramework="netstandard1.3">
|
||||
@@ -32,33 +31,43 @@
|
||||
<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="uap10.0">
|
||||
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.1.9" />
|
||||
</group>
|
||||
<group targetFramework="net452">
|
||||
</group>
|
||||
<group targetFramework="net461">
|
||||
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.2.10" />
|
||||
</group>
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- License -->
|
||||
<file src="..\LICENSE" />
|
||||
|
||||
<!-- Images -->
|
||||
<file src="..\Images\Logo_128x128.png" target="images\" />
|
||||
|
||||
<!-- .NET Standard 1.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\"/>
|
||||
|
||||
<!-- .NET Standard 2.1 -->
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.1\MQTTnet.*" target="lib\netstandard2.1\"/>
|
||||
|
||||
<!-- Universal Windows -->
|
||||
<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\netstandard2.0\MQTTnet.*" target="lib\net461\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net462\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net470\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net471\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net472\"/>
|
||||
<!-- .NET Framework 4.6.0 will use binaries from 4.5.2. -->
|
||||
<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>
|
||||
</package>
|
||||
</package>
|
||||
|
||||
@@ -3,49 +3,131 @@ param([string]$assemblyVersion, [string]$nugetVersion)
|
||||
if ([string]::IsNullOrEmpty($assemblyVersion)) {$assemblyVersion = "0.0.1"}
|
||||
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
|
||||
|
||||
Write-Host
|
||||
Write-Host "Assembly version = $assemblyVersion"
|
||||
Write-Host "Nuget version = $nugetVersion"
|
||||
Write-Host "MSBuild path = $msbuild"
|
||||
Write-Host
|
||||
|
||||
$vswhere = ${Env:\ProgramFiles(x86)} + '\Microsoft Visual Studio\Installer\vswhere'
|
||||
Invoke-WebRequest -Uri "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -OutFile "nuget.exe"
|
||||
|
||||
$path = &$vswhere -latest -products * -requires Microsoft.Component.MSBuild -property installationPath
|
||||
if ($path) {
|
||||
$msbuild = join-path $path 'MSBuild\15.0\Bin\MSBuild.exe'
|
||||
.\nuget.exe restore ..\MQTTnet.sln
|
||||
|
||||
# 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"
|
||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /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="netstandard1.3" /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="netstandard2.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="uap10.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"
|
||||
# Build and execute tests
|
||||
&$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
|
||||
|
||||
# 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"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /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="netstandard1.3" /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.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="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
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
|
||||
|
||||
# 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"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /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="netstandard1.3" /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.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="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
# 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"
|
||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /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="netstandard1.3" /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="netstandard2.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="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="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
|
||||
Remove-Item .\NuGet -Force -Recurse -ErrorAction SilentlyContinue
|
||||
# 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"
|
||||
|
||||
(Get-Content MQTTnet.AspNetCore.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.AspNetCore.nuspec
|
||||
(Get-Content MQTTnet.Extensions.Rpc.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.Rpc.nuspec
|
||||
(Get-Content MQTTnet.Extensions.ManagedClient.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.ManagedClient.nuspec
|
||||
|
||||
New-Item -ItemType Directory -Force -Path .\NuGet
|
||||
.\nuget.exe pack MQTTnet.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.AspNetCore.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.Extensions.Rpc.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.Extensions.ManagedClient.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
}
|
||||
# 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"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /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="netstandard1.3" /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.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"
|
||||
|
||||
# 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"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /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="netstandard1.3" /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.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"
|
||||
|
||||
# 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"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /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="netstandard1.3" /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.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"
|
||||
|
||||
# Create NuGet packages.
|
||||
|
||||
Remove-Item .\NuGet -Force -Recurse -ErrorAction SilentlyContinue
|
||||
|
||||
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
|
||||
(Get-Content MQTTnet.Extensions.Rpc.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.Rpc.nuspec
|
||||
Copy-Item MQTTnet.Extensions.ManagedClient.nuspec -Destination MQTTnet.Extensions.ManagedClient.nuspec.old -Force
|
||||
(Get-Content MQTTnet.Extensions.ManagedClient.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.ManagedClient.nuspec
|
||||
Copy-Item MQTTnet.Extensions.WebSocket4Net.nuspec -Destination MQTTnet.Extensions.WebSocket4Net.nuspec.old -Force
|
||||
(Get-Content MQTTnet.Extensions.WebSocket4Net.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.WebSocket4Net.nuspec
|
||||
|
||||
New-Item -ItemType Directory -Force -Path .\NuGet
|
||||
.\nuget.exe pack MQTTnet.nuspec -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.AspNetCore.nuspec -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.Extensions.Rpc.nuspec -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\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.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)
|
||||
|
||||
####################################################################
|
||||
@@ -1,9 +1,13 @@
|
||||
param([string]$apiKey)
|
||||
|
||||
Invoke-WebRequest -Uri "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -OutFile "nuget.exe"
|
||||
|
||||
$files = Get-ChildItem -Path ".\NuGet" -Filter "*.nupkg"
|
||||
foreach ($file in $files)
|
||||
{
|
||||
Write-Host "Uploading: " $file
|
||||
|
||||
.\nuget.exe push $file.Fullname $apiKey -NoSymbols -Source https://api.nuget.org/v3/index.json
|
||||
}
|
||||
.\nuget.exe push $file.Fullname $apiKey -Source https://api.nuget.org/v3/index.json
|
||||
}
|
||||
|
||||
Remove-Item "nuget.exe" -Force -Recurse -ErrorAction SilentlyContinue
|
||||
@@ -1,9 +1,9 @@
|
||||
# Import codeSigningKey.pfx
|
||||
In order to import the key for code signing on a new developer machine use the following command within the VS developer command line:
|
||||
|
||||
> sn –i codeSigningKey.pfx VS_KEY_EFCA4C5B6DFD4B4F
|
||||
sn –i .\codeSigningKey.pfx VS_KEY_62CA73019ED23333
|
||||
|
||||
The container name may be different.
|
||||
|
||||
# Check if the assembly has a strong name
|
||||
> sn -vf MQTTnet.dll
|
||||
sn -vf MQTTnet.dll
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,239 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="210mm"
|
||||
height="297mm"
|
||||
viewBox="0 0 210 297"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
sodipodi:docname="MQTT.svg">
|
||||
<defs
|
||||
id="defs2">
|
||||
<filter
|
||||
inkscape:label="Blur Double"
|
||||
inkscape:menu="Blurs"
|
||||
inkscape:menu-tooltip="Overlays two copies with different blur amounts and modifiable blend and composite"
|
||||
style="color-interpolation-filters:sRGB;"
|
||||
id="filter3801">
|
||||
<feGaussianBlur
|
||||
stdDeviation="5"
|
||||
result="fbSourceGraphic"
|
||||
id="feGaussianBlur3793" />
|
||||
<feGaussianBlur
|
||||
stdDeviation="0.01"
|
||||
in="SourceGraphic"
|
||||
result="result1"
|
||||
id="feGaussianBlur3795" />
|
||||
<feComposite
|
||||
in2="result1"
|
||||
operator="arithmetic"
|
||||
in="fbSourceGraphic"
|
||||
k2="0.5"
|
||||
k3="0.5"
|
||||
result="result2"
|
||||
id="feComposite3797" />
|
||||
<feBlend
|
||||
in2="fbSourceGraphic"
|
||||
mode="normal"
|
||||
result="result3"
|
||||
id="feBlend3799" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.24748737"
|
||||
inkscape:cx="955.72999"
|
||||
inkscape:cy="295.38958"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="3840"
|
||||
inkscape:window-height="2066"
|
||||
inkscape:window-x="2989"
|
||||
inkscape:window-y="-11"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
ry="50"
|
||||
rx="50"
|
||||
style="fill:#000000;stroke-width:0.26458332;filter:url(#filter3801)"
|
||||
y="92.501205"
|
||||
x="56.367012"
|
||||
height="512"
|
||||
width="512"
|
||||
id="rect3791"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001" />
|
||||
<rect
|
||||
id="rect3713"
|
||||
width="512"
|
||||
height="512"
|
||||
x="53.721176"
|
||||
y="88.797035"
|
||||
style="fill:#aa0000;stroke-width:0.26458332"
|
||||
rx="50"
|
||||
ry="50"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001" />
|
||||
<rect
|
||||
style="fill:#ce0000;fill-opacity:0.60591131;stroke-width:0.28100395"
|
||||
id="rect3806"
|
||||
width="143.25298"
|
||||
height="9.0714283"
|
||||
x="270.81995"
|
||||
y="554.02386"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4638"
|
||||
d="m 494.14104,142.00092 -5.35736,41.28822 67.77684,374.38977 a 263.46279,229.17651 36.835702 0 1 4.98278,-0.4605 263.46279,229.17651 36.835702 0 1 7.77403,0.0471 z"
|
||||
style="fill:#ce0000;fill-opacity:0.60591131;stroke-width:0.59952348"
|
||||
inkscape:transform-center-x="-28.818481"
|
||||
inkscape:transform-center-y="146.97441"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001" />
|
||||
<path
|
||||
style="fill:#d40000;stroke-width:1.76103246"
|
||||
d="m 2138.1582,1411.377 a 995.76488,866.17897 0 0 0 -842.3984,854.3418 995.76488,866.17897 0 0 0 0.2988,5.0097 h 653.123 c 104.693,0 188.9766,-84.2836 188.9766,-188.9765 z"
|
||||
transform="scale(0.26458333)"
|
||||
id="path3715-5-8"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001" />
|
||||
<ellipse
|
||||
style="fill:#d40000;stroke-width:0.06901014"
|
||||
id="path3715-5"
|
||||
cx="492.58557"
|
||||
cy="170.58017"
|
||||
rx="39.021355"
|
||||
ry="33.94323"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001" />
|
||||
<path
|
||||
style="fill:#ce0000;fill-opacity:0.60591131;stroke-width:2.26591563"
|
||||
d="M 464.66602,532.89453 542.01562,669.92969 1595.373,1648.873 a 995.76487,866.17896 0 0 1 14.0293,-12.6835 995.76487,866.17896 0 0 1 23.6231,-17.4727 z"
|
||||
id="rect3806-2"
|
||||
inkscape:connector-curvature="0"
|
||||
transform="scale(0.26458333)"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
||||
x="102.34985"
|
||||
y="320.75201"
|
||||
id="text3774"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3772"
|
||||
x="102.34985"
|
||||
y="320.75201"
|
||||
style="font-size:141.1111145px;fill:#ffffff;stroke-width:0.26458332">MQTT</tspan></text>
|
||||
<ellipse
|
||||
ry="33.94323"
|
||||
rx="39.021355"
|
||||
cy="148.56271"
|
||||
cx="124.47787"
|
||||
id="ellipse3768"
|
||||
style="fill:#d40000;stroke-width:0.06901014"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001" />
|
||||
<ellipse
|
||||
style="fill:#d40000;stroke-width:0.06901014"
|
||||
id="ellipse3770"
|
||||
cx="234.9953"
|
||||
cy="556.91571"
|
||||
rx="39.021355"
|
||||
ry="33.94323"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001" />
|
||||
<text
|
||||
id="text3778"
|
||||
y="474.73837"
|
||||
x="194.57605"
|
||||
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
||||
xml:space="preserve"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001"><tspan
|
||||
style="font-size:141.1111145px;fill:#ffffff;stroke-width:0.26458332"
|
||||
y="474.73837"
|
||||
x="194.57605"
|
||||
id="tspan3776"
|
||||
sodipodi:role="line">net</tspan></text>
|
||||
<rect
|
||||
ry="50"
|
||||
rx="50"
|
||||
style="fill:#aa0000;stroke-width:0.26458332"
|
||||
y="97.349655"
|
||||
x="-672.18286"
|
||||
height="512"
|
||||
width="512"
|
||||
id="rect4642"
|
||||
inkscape:export-xdpi="6.3499999"
|
||||
inkscape:export-ydpi="6.3499999" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4648"
|
||||
d="m -160.18302,381.97945 a 263.46279,229.17652 0 0 0 -222.88457,226.0446 263.46279,229.17652 0 0 0 0.0791,1.32549 h 172.80546 c 27.70002,0 50.00005,-22.30004 50.00005,-50.00004 z"
|
||||
style="fill:#d40000;stroke-width:0.46593982"
|
||||
inkscape:export-xdpi="6.3499999"
|
||||
inkscape:export-ydpi="6.3499999" />
|
||||
<text
|
||||
id="text4656"
|
||||
y="329.30466"
|
||||
x="-639.42944"
|
||||
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
||||
xml:space="preserve"
|
||||
inkscape:export-xdpi="6.3499999"
|
||||
inkscape:export-ydpi="6.3499999"><tspan
|
||||
style="font-weight:bold;font-size:141.1111145px;fill:#ffffff;stroke-width:0.26458332"
|
||||
y="329.30466"
|
||||
x="-639.42944"
|
||||
id="tspan4654"
|
||||
sodipodi:role="line">MQTT</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
||||
x="-550.90747"
|
||||
y="482.23267"
|
||||
id="text4664"
|
||||
inkscape:export-xdpi="6.3499999"
|
||||
inkscape:export-ydpi="6.3499999"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4662"
|
||||
x="-550.90747"
|
||||
y="482.23267"
|
||||
style="font-weight:bold;font-size:141.1111145px;fill:#ffffff;stroke-width:0.26458332">net</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Christian Kratky
|
||||
Copyright (c) 2016-2019 Christian Kratky
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27004.2010
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Core.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Core.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1-B9D6-40BF-81EC-86004D7765B4}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{32A630A7-2598-41D7-B625-204CD906F5FB}"
|
||||
@@ -45,6 +43,12 @@ 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.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
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -57,22 +61,6 @@ Global
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{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
|
||||
{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
|
||||
@@ -201,12 +189,59 @@ 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
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|x64.Build.0 = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|x86.Build.0 = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|x64.Build.0 = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|x86.Build.0 = Release|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|x64.Build.0 = Release|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{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}
|
||||
@@ -215,6 +250,9 @@ 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}
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27004.2010
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29009.5
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}"
|
||||
EndProject
|
||||
@@ -19,6 +19,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{67C28AC1
|
||||
Build\MQTTnet.AspNetCore.nuspec = Build\MQTTnet.AspNetCore.nuspec
|
||||
Build\MQTTnet.Extensions.ManagedClient.nuspec = Build\MQTTnet.Extensions.ManagedClient.nuspec
|
||||
Build\MQTTnet.Extensions.Rpc.nuspec = Build\MQTTnet.Extensions.Rpc.nuspec
|
||||
Build\MQTTnet.Extensions.WebSocket4Net.nuspec = Build\MQTTnet.Extensions.WebSocket4Net.nuspec
|
||||
Build\MQTTnet.nuspec = Build\MQTTnet.nuspec
|
||||
Build\upload.ps1 = Build\upload.ps1
|
||||
EndProjectSection
|
||||
@@ -26,6 +27,7 @@ EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B3F60ECB-45BA-4C66-8903-8BB89CA67998}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.bettercodehub.yml = .bettercodehub.yml
|
||||
.editorconfig = .editorconfig
|
||||
appveyor.yml = appveyor.yml
|
||||
LICENSE = LICENSE
|
||||
README.md = README.md
|
||||
@@ -47,6 +49,12 @@ 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
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -226,13 +234,45 @@ 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
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED} = {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}
|
||||
@@ -241,6 +281,8 @@ 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}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<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>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<p align="center">
|
||||
<img src="https://github.com/chkr1011/MQTTnet/blob/master/Images/Logo_128x128.png?raw=true" width="128">
|
||||
<img src="https://github.com/chkr1011/MQTTnet/blob/master/Images/icon_det_256.png?raw=true" width="196">
|
||||
<br/>
|
||||
<br/>
|
||||
</p>
|
||||
@@ -7,8 +7,8 @@
|
||||
[](https://www.nuget.org/packages/MQTTnet)
|
||||
[](https://ci.appveyor.com/project/chkr1011/mqttnet)
|
||||
[](https://bettercodehub.com/)
|
||||
[](https://opencollective.com/mqttnet)
|
||||
[](https://opencollective.com/mqttnet)
|
||||
[](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)
|
||||
|
||||
# MQTTnet
|
||||
|
||||
@@ -26,14 +26,15 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
|
||||
* Uniform API across all supported versions of the MQTT protocol
|
||||
* Interfaces included for mocking and testing
|
||||
* Access to internal trace messages
|
||||
* Unit tested (~120 tests)
|
||||
* Unit tested (~250 tests)
|
||||
* No external dependencies
|
||||
|
||||
\* Tested on local machine (Intel i7 8700K) with MQTTnet client and server running in the same process using the TCP channel. The app for verification is part of this repository and stored in _/Tests/MQTTnet.TestApp.NetCore_.
|
||||
|
||||
### Client
|
||||
|
||||
* Communication via TCP (+TLS) or WS (WebSocket) supported
|
||||
* Included core _MqttClient_ with low level functionality
|
||||
* Included core _LowLevelMqttClient_ with low level functionality
|
||||
* Also included _ManagedMqttClient_ which maintains the connection and subscriptions automatically. Also application messages are queued and re-scheduled for higher QoS levels automatically.
|
||||
* Rx support (via another project)
|
||||
* Compatible with Microsoft Azure IoT Hub
|
||||
@@ -51,6 +52,16 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
|
||||
* Validate subscriptions and deny subscribing of certain topics depending on requesting clients
|
||||
* Connect clients with different protocol versions at the same time.
|
||||
|
||||
## MQTTnet Server
|
||||
|
||||
_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
|
||||
* 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)
|
||||
* Extensive configuration parameters and customization supported
|
||||
|
||||
## Supported frameworks
|
||||
|
||||
* .NET Standard 1.3+
|
||||
@@ -81,8 +92,6 @@ Please find examples and the documentation at the Wiki of this repository (<http
|
||||
If you want to contribute to this project just create a pull request. But only pull requests which are matching the code style of this library will be accepted. Before creating a pull request please have a look at the library to get an overview of the required style.
|
||||
Also additions and updates in the Wiki are welcome.
|
||||
|
||||
This project also listed at Open Collective (https://opencollective.com/mqttnet).
|
||||
|
||||
## References
|
||||
|
||||
This library is used in the following projects:
|
||||
@@ -93,11 +102,15 @@ This library is used in the following projects:
|
||||
* 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))
|
||||
* Wirehome.Core (Open Source Home Automation system for .NET Core, <https://github.com/chkr1011/Wirehome.Core>)
|
||||
|
||||
Further projects using this project can be found under https://github.com/chkr1011/MQTTnet/network/dependents.
|
||||
|
||||
If you use this library and want to see your project here please create a pull request.
|
||||
|
||||
## MIT License
|
||||
## License
|
||||
|
||||
Copyright (c) 2017-2019 Christian Kratky
|
||||
MIT License
|
||||
|
||||
MQTTnet Copyright (c) 2016-2020 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
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.AspNetCore.Client.Tcp;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Formatter;
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace MQTTnet.AspNetCore.Client
|
||||
{
|
||||
public class MqttClientConnectionContextFactory : IMqttClientAdapterFactory
|
||||
{
|
||||
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetChildLogger logger)
|
||||
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
@@ -21,7 +19,10 @@ namespace MQTTnet.AspNetCore.Client
|
||||
{
|
||||
var endpoint = new DnsEndPoint(tcpOptions.Server, tcpOptions.GetPort());
|
||||
var tcpConnection = new TcpConnection(endpoint);
|
||||
return new MqttConnectionContext(new MqttPacketFormatterAdapter(options.ProtocolVersion), tcpConnection);
|
||||
|
||||
var writer = new SpanBasedMqttPacketWriter();
|
||||
var formatter = new MqttPacketFormatterAdapter(options.ProtocolVersion, writer);
|
||||
return new MqttConnectionContext(formatter, tcpConnection);
|
||||
}
|
||||
default:
|
||||
{
|
||||
|
||||
@@ -5,6 +5,10 @@ using System.Diagnostics;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net.Sockets;
|
||||
|
||||
#if NETCOREAPP2_1
|
||||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
|
||||
namespace MQTTnet.AspNetCore.Client.Tcp
|
||||
{
|
||||
public class SocketSender
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using MQTTnet.Exceptions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using MQTTnet.Exceptions;
|
||||
|
||||
namespace MQTTnet.AspNetCore.Client.Tcp
|
||||
{
|
||||
@@ -40,8 +40,11 @@ namespace MQTTnet.AspNetCore.Client.Tcp
|
||||
_sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
|
||||
_receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
|
||||
}
|
||||
|
||||
#if NETCOREAPP3_1
|
||||
public override ValueTask DisposeAsync()
|
||||
#else
|
||||
public Task DisposeAsync()
|
||||
#endif
|
||||
{
|
||||
IsConnected = false;
|
||||
|
||||
@@ -50,8 +53,15 @@ namespace MQTTnet.AspNetCore.Client.Tcp
|
||||
|
||||
_socket?.Dispose();
|
||||
|
||||
#if NETCOREAPP3_1
|
||||
|
||||
return base.DisposeAsync();
|
||||
}
|
||||
#else
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
#endif
|
||||
|
||||
public async Task StartAsync()
|
||||
{
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Linq;
|
||||
using MQTTnet.Server;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public static class ApplicationBuilderExtensions
|
||||
{
|
||||
[Obsolete("This class is obsolete and will be removed in a future version. The recommended alternative is to use MapMqtt inside Microsoft.AspNetCore.Builder.UseEndpoints(...).")]
|
||||
public static IApplicationBuilder UseMqttEndpoint(this IApplicationBuilder app, string path = "/mqtt")
|
||||
{
|
||||
app.UseWebSockets();
|
||||
@@ -24,28 +23,19 @@ namespace MQTTnet.AspNetCore
|
||||
|
||||
if (context.Request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
|
||||
{
|
||||
subProtocol = SelectSubProtocol(requestedSubProtocolValues);
|
||||
subProtocol = MqttSubProtocolSelector.SelectSubProtocol(requestedSubProtocolValues);
|
||||
}
|
||||
|
||||
var adapter = app.ApplicationServices.GetRequiredService<MqttWebSocketServerAdapter>();
|
||||
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(subProtocol))
|
||||
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(subProtocol).ConfigureAwait(false))
|
||||
{
|
||||
var endpoint = $"{context.Connection.RemoteIpAddress}:{context.Connection.RemotePort}";
|
||||
await adapter.RunWebSocketConnectionAsync(webSocket, endpoint);
|
||||
await adapter.RunWebSocketConnectionAsync(webSocket, context);
|
||||
}
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
public static string SelectSubProtocol(IList<string> requestedSubProtocolValues)
|
||||
{
|
||||
// Order the protocols to also match "mqtt", "mqttv-3.1", "mqttv-3.11" etc.
|
||||
return requestedSubProtocolValues
|
||||
.OrderByDescending(p => p.Length)
|
||||
.FirstOrDefault(p => p.ToLower().StartsWith("mqtt"));
|
||||
}
|
||||
|
||||
public static IApplicationBuilder UseMqttServer(this IApplicationBuilder app, Action<IMqttServer> configure)
|
||||
{
|
||||
var server = app.ApplicationServices.GetRequiredService<IMqttServer>();
|
||||
@@ -0,0 +1,20 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Connections;
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public static class ConnectionRouteBuilderExtensions
|
||||
{
|
||||
#if NETCOREAPP3_1
|
||||
[Obsolete("This class is obsolete and will be removed in a future version. The recommended alternative is to use MapMqtt inside Microsoft.AspNetCore.Builder.UseEndpoints(...).")]
|
||||
#endif
|
||||
public static void MapMqtt(this ConnectionsRouteBuilder connection, PathString path)
|
||||
{
|
||||
connection.MapConnectionHandler<MqttConnectionHandler>(path, options =>
|
||||
{
|
||||
options.WebSockets.SubProtocolSelector = MqttSubProtocolSelector.SelectSubProtocol;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
#if NETCOREAPP3_1
|
||||
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public static class EndpointRouterExtensions
|
||||
{
|
||||
public static void MapMqtt(this IEndpointRouteBuilder endpoints, string pattern)
|
||||
{
|
||||
endpoints.MapConnectionHandler<MqttConnectionHandler>(pattern, options =>
|
||||
{
|
||||
options.WebSockets.SubProtocolSelector = MqttSubProtocolSelector.SelectSubProtocol;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -9,13 +9,20 @@ namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public static class ReaderExtensions
|
||||
{
|
||||
public static bool TryDecode(this MqttPacketFormatterAdapter formatter, in ReadOnlySequence<byte> input, out MqttBasePacket packet, out SequencePosition consumed, out SequencePosition observed)
|
||||
public static bool TryDecode(this MqttPacketFormatterAdapter formatter,
|
||||
SpanBasedMqttPacketBodyReader reader,
|
||||
in ReadOnlySequence<byte> input,
|
||||
out MqttBasePacket packet,
|
||||
out SequencePosition consumed,
|
||||
out SequencePosition observed,
|
||||
out int bytesRead)
|
||||
{
|
||||
if (formatter == null) throw new ArgumentNullException(nameof(formatter));
|
||||
|
||||
packet = null;
|
||||
consumed = input.Start;
|
||||
observed = input.End;
|
||||
bytesRead = 0;
|
||||
var copy = input;
|
||||
|
||||
if (copy.Length < 2)
|
||||
@@ -24,7 +31,7 @@ namespace MQTTnet.AspNetCore
|
||||
}
|
||||
|
||||
var fixedheader = copy.First.Span[0];
|
||||
if (!TryReadBodyLength(ref copy, out var bodyLength))
|
||||
if (!TryReadBodyLength(ref copy, out int headerLength, out var bodyLength))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -35,34 +42,45 @@ namespace MQTTnet.AspNetCore
|
||||
}
|
||||
|
||||
var bodySlice = copy.Slice(0, bodyLength);
|
||||
var buffer = bodySlice.GetArray();
|
||||
packet = formatter.Decode(new ReceivedMqttPacket(fixedheader, new MqttPacketBodyReader(buffer, 0, buffer.Length), buffer.Length + 2));
|
||||
var buffer = bodySlice.GetMemory();
|
||||
reader.SetBuffer(buffer);
|
||||
|
||||
var receivedMqttPacket = new ReceivedMqttPacket(fixedheader, reader, buffer.Length + 2);
|
||||
|
||||
if (formatter.ProtocolVersion == MqttProtocolVersion.Unknown)
|
||||
{
|
||||
formatter.DetectProtocolVersion(receivedMqttPacket);
|
||||
}
|
||||
|
||||
packet = formatter.Decode(receivedMqttPacket);
|
||||
consumed = bodySlice.End;
|
||||
observed = bodySlice.End;
|
||||
bytesRead = headerLength + bodyLength;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static byte[] GetArray(this in ReadOnlySequence<byte> input)
|
||||
private static ReadOnlyMemory<byte> GetMemory(this in ReadOnlySequence<byte> input)
|
||||
{
|
||||
if (input.IsSingleSegment)
|
||||
{
|
||||
return input.First.Span.ToArray();
|
||||
return input.First;
|
||||
}
|
||||
|
||||
// Should be rare
|
||||
return input.ToArray();
|
||||
}
|
||||
|
||||
private static bool TryReadBodyLength(ref ReadOnlySequence<byte> input, out int result)
|
||||
private static bool TryReadBodyLength(ref ReadOnlySequence<byte> input, out int headerLength, out int bodyLength)
|
||||
{
|
||||
// Alorithm taken from https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html.
|
||||
var multiplier = 1;
|
||||
var value = 0;
|
||||
byte encodedByte;
|
||||
var index = 1;
|
||||
result = 0;
|
||||
|
||||
var temp = input.Slice(0, Math.Min(5, input.Length)).GetArray();
|
||||
headerLength = 0;
|
||||
bodyLength = 0;
|
||||
|
||||
var temp = input.Slice(0, Math.Min(5, input.Length)).GetMemory().Span;
|
||||
|
||||
do
|
||||
{
|
||||
@@ -76,7 +94,7 @@ namespace MQTTnet.AspNetCore
|
||||
value += (byte)(encodedByte & 127) * multiplier;
|
||||
if (multiplier > 128 * 128 * 128)
|
||||
{
|
||||
throw new MqttProtocolViolationException($"Remaining length is invalid (Data={string.Join(",", temp.AsSpan(1, index).ToArray())}).");
|
||||
throw new MqttProtocolViolationException($"Remaining length is invalid (Data={string.Join(",", temp.Slice(1, index).ToArray())}).");
|
||||
}
|
||||
|
||||
multiplier *= 128;
|
||||
@@ -84,7 +102,8 @@ namespace MQTTnet.AspNetCore
|
||||
|
||||
input = input.Slice(index);
|
||||
|
||||
result = value;
|
||||
headerLength = index;
|
||||
bodyLength = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Server;
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
@@ -13,7 +13,7 @@ namespace MQTTnet.AspNetCore
|
||||
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, IMqttServerOptions options)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
|
||||
services.AddSingleton(options);
|
||||
|
||||
services.AddHostedMqttServer();
|
||||
@@ -23,7 +23,8 @@ namespace MQTTnet.AspNetCore
|
||||
|
||||
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, Action<MqttServerOptionsBuilder> configure)
|
||||
{
|
||||
services.AddSingleton<IMqttServerOptions>(s => {
|
||||
services.AddSingleton<IMqttServerOptions>(s =>
|
||||
{
|
||||
var builder = new MqttServerOptionsBuilder();
|
||||
configure(builder);
|
||||
return builder.Build();
|
||||
@@ -36,7 +37,8 @@ namespace MQTTnet.AspNetCore
|
||||
|
||||
public static IServiceCollection AddHostedMqttServerWithServices(this IServiceCollection services, Action<AspNetMqttServerOptionsBuilder> configure)
|
||||
{
|
||||
services.AddSingleton<IMqttServerOptions>(s => {
|
||||
services.AddSingleton<IMqttServerOptions>(s =>
|
||||
{
|
||||
var builder = new AspNetMqttServerOptionsBuilder(s);
|
||||
configure(builder);
|
||||
return builder.Build();
|
||||
@@ -60,14 +62,12 @@ namespace MQTTnet.AspNetCore
|
||||
private static IServiceCollection AddHostedMqttServer(this IServiceCollection services)
|
||||
{
|
||||
var logger = new MqttNetLogger();
|
||||
var childLogger = logger.CreateChildLogger();
|
||||
|
||||
services.AddSingleton<IMqttNetLogger>(logger);
|
||||
services.AddSingleton(childLogger);
|
||||
services.AddSingleton<MqttHostedServer>();
|
||||
services.AddSingleton<IHostedService>(s => s.GetService<MqttHostedServer>());
|
||||
services.AddSingleton<IMqttServer>(s => s.GetService<MqttHostedServer>());
|
||||
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<TargetFrameworks>netstandard2.0;netcoreapp2.1;netcoreapp3.1</TargetFrameworks>
|
||||
<Product />
|
||||
<Company />
|
||||
<Authors />
|
||||
<PackageId />
|
||||
<LangVersion>7.2</LangVersion>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DefineConstants>RELEASE;NETSTANDARD2_0</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="2.1.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.1.1" />
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp3.1'">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Connections" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Http.Connections.Features;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.AspNetCore.Client.Tcp;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using System;
|
||||
using System.IO.Pipelines;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
@@ -17,29 +19,72 @@ namespace MQTTnet.AspNetCore
|
||||
{
|
||||
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
|
||||
Connection = connection ?? throw new ArgumentNullException(nameof(connection));
|
||||
|
||||
if (Connection.Transport != null)
|
||||
{
|
||||
_input = Connection.Transport.Input;
|
||||
_output = Connection.Transport.Output;
|
||||
}
|
||||
|
||||
_reader = new SpanBasedMqttPacketBodyReader();
|
||||
}
|
||||
|
||||
public string Endpoint => Connection.ConnectionId;
|
||||
private PipeReader _input;
|
||||
private PipeWriter _output;
|
||||
private readonly SpanBasedMqttPacketBodyReader _reader;
|
||||
|
||||
public string Endpoint
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NETCOREAPP3_1
|
||||
if (Connection?.RemoteEndPoint != null)
|
||||
{
|
||||
return Connection.RemoteEndPoint.ToString();
|
||||
}
|
||||
#endif
|
||||
var connection = Http?.HttpContext?.Connection;
|
||||
if (connection == null)
|
||||
{
|
||||
return Connection.ConnectionId;
|
||||
}
|
||||
|
||||
return $"{connection.RemoteIpAddress}:{connection.RemotePort}";
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSecureConnection => Http?.HttpContext?.Request?.IsHttps ?? false;
|
||||
|
||||
public X509Certificate2 ClientCertificate => Http?.HttpContext?.Connection?.ClientCertificate;
|
||||
|
||||
private IHttpContextFeature Http => Connection.Features.Get<IHttpContextFeature>();
|
||||
|
||||
public ConnectionContext Connection { get; }
|
||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||
public event EventHandler ReadingPacketStarted;
|
||||
public event EventHandler ReadingPacketCompleted;
|
||||
|
||||
public long BytesSent { get; set; }
|
||||
public long BytesReceived { get; set; }
|
||||
|
||||
public Action ReadingPacketStartedCallback { get; set; }
|
||||
public Action ReadingPacketCompletedCallback { get; set; }
|
||||
|
||||
private readonly SemaphoreSlim _writerSemaphore = new SemaphoreSlim(1, 1);
|
||||
|
||||
public Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||
public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
if (Connection is TcpConnection tcp && !tcp.IsConnected)
|
||||
{
|
||||
return tcp.StartAsync();
|
||||
await tcp.StartAsync().ConfigureAwait(false);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
|
||||
_input = Connection.Transport.Input;
|
||||
_output = Connection.Transport.Output;
|
||||
}
|
||||
|
||||
public Task DisconnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
Connection.Transport.Input.Complete();
|
||||
Connection.Transport.Output.Complete();
|
||||
_input?.Complete();
|
||||
_output?.Complete();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
@@ -72,14 +117,15 @@ namespace MQTTnet.AspNetCore
|
||||
{
|
||||
if (!buffer.IsEmpty)
|
||||
{
|
||||
if (PacketFormatterAdapter.TryDecode(buffer, out var packet, out consumed, out observed))
|
||||
if (PacketFormatterAdapter.TryDecode(_reader, buffer, out var packet, out consumed, out observed, out var received))
|
||||
{
|
||||
BytesReceived += received;
|
||||
return packet;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we did receive something but the message is not yet complete
|
||||
ReadingPacketStarted?.Invoke(this, EventArgs.Empty);
|
||||
ReadingPacketStartedCallback?.Invoke();
|
||||
}
|
||||
}
|
||||
else if (readResult.IsCompleted)
|
||||
@@ -98,22 +144,36 @@ namespace MQTTnet.AspNetCore
|
||||
}
|
||||
finally
|
||||
{
|
||||
ReadingPacketCompleted?.Invoke(this, EventArgs.Empty);
|
||||
ReadingPacketCompletedCallback?.Invoke();
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
|
||||
public void ResetStatistics()
|
||||
{
|
||||
var buffer = PacketFormatterAdapter.Encode(packet).AsMemory();
|
||||
var output = Connection.Transport.Output;
|
||||
BytesReceived = 0;
|
||||
BytesSent = 0;
|
||||
}
|
||||
|
||||
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
var formatter = PacketFormatterAdapter;
|
||||
var buffer = formatter.Encode(packet);
|
||||
var msg = buffer.AsMemory();
|
||||
var output = _output;
|
||||
|
||||
await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await output.WriteAsync(buffer, cancellationToken).ConfigureAwait(false);
|
||||
var result = await output.WriteAsync(msg, cancellationToken).ConfigureAwait(false);
|
||||
if (result.IsCompleted)
|
||||
{
|
||||
BytesSent += msg.Length;
|
||||
}
|
||||
|
||||
PacketFormatterAdapter.FreeBuffer();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public class MqttConnectionHandler : ConnectionHandler, IMqttServerAdapter
|
||||
{
|
||||
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
|
||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
|
||||
public override async Task OnConnectedAsync(ConnectionContext connection)
|
||||
{
|
||||
@@ -21,12 +21,15 @@ namespace MQTTnet.AspNetCore
|
||||
transferFormatFeature.ActiveFormat = TransferFormat.Binary;
|
||||
}
|
||||
|
||||
using (var adapter = new MqttConnectionContext(new MqttPacketFormatterAdapter(), connection))
|
||||
var writer = new SpanBasedMqttPacketWriter();
|
||||
var formatter = new MqttPacketFormatterAdapter(writer);
|
||||
using (var adapter = new MqttConnectionContext(formatter, connection))
|
||||
{
|
||||
var args = new MqttServerAdapterClientAcceptedEventArgs(adapter);
|
||||
ClientAccepted?.Invoke(this, args);
|
||||
|
||||
await args.SessionTask;
|
||||
var clientHandler = ClientHandler;
|
||||
if (clientHandler != null)
|
||||
{
|
||||
await clientHandler(adapter).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,14 +14,15 @@ namespace MQTTnet.AspNetCore
|
||||
private readonly IMqttServerOptions _options;
|
||||
|
||||
public MqttHostedServer(IMqttServerOptions options, IEnumerable<IMqttServerAdapter> adapters, IMqttNetLogger logger)
|
||||
: base(adapters, logger.CreateChildLogger(nameof(MqttHostedServer)))
|
||||
: base(adapters, logger)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return StartAsync(_options);
|
||||
_ = StartAsync(_options);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public static class MqttSubProtocolSelector
|
||||
{
|
||||
public static string SelectSubProtocol(IList<string> requestedSubProtocolValues)
|
||||
{
|
||||
// Order the protocols to also match "mqtt", "mqttv-3.1", "mqttv-3.11" etc.
|
||||
return requestedSubProtocolValues
|
||||
.OrderByDescending(p => p.Length)
|
||||
.FirstOrDefault(p => p.ToLower().StartsWith("mqtt"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,25 @@
|
||||
using System;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading.Tasks;
|
||||
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;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public class MqttWebSocketServerAdapter : IMqttServerAdapter
|
||||
public sealed class MqttWebSocketServerAdapter : IMqttServerAdapter
|
||||
{
|
||||
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
|
||||
readonly IMqttNetLogger _rootLogger;
|
||||
|
||||
public MqttWebSocketServerAdapter(IMqttNetLogger logger)
|
||||
{
|
||||
_rootLogger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
|
||||
public Task StartAsync(IMqttServerOptions options)
|
||||
{
|
||||
@@ -23,24 +31,38 @@ namespace MQTTnet.AspNetCore
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task RunWebSocketConnectionAsync(WebSocket webSocket, string endpoint)
|
||||
public async Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
|
||||
{
|
||||
if (webSocket == null) throw new ArgumentNullException(nameof(webSocket));
|
||||
|
||||
var clientAdapter = new MqttChannelAdapter(new MqttWebSocketChannel(webSocket, endpoint), new MqttPacketFormatterAdapter(), new MqttNetLogger().CreateChildLogger(nameof(MqttWebSocketServerAdapter)));
|
||||
var endpoint = $"{httpContext.Connection.RemoteIpAddress}:{httpContext.Connection.RemotePort}";
|
||||
|
||||
var eventArgs = new MqttServerAdapterClientAcceptedEventArgs(clientAdapter);
|
||||
ClientAccepted?.Invoke(this, eventArgs);
|
||||
|
||||
if (eventArgs.SessionTask != null)
|
||||
var clientCertificate = await httpContext.Connection.GetClientCertificateAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await eventArgs.SessionTask.ConfigureAwait(false);
|
||||
var isSecureConnection = clientCertificate != null;
|
||||
|
||||
var clientHandler = ClientHandler;
|
||||
if (clientHandler != null)
|
||||
{
|
||||
var writer = new SpanBasedMqttPacketWriter();
|
||||
var formatter = new MqttPacketFormatterAdapter(writer);
|
||||
var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection, clientCertificate);
|
||||
|
||||
using (var channelAdapter = new MqttChannelAdapter(channel, formatter, _rootLogger))
|
||||
{
|
||||
await clientHandler(channelAdapter).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
clientCertificate?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
StopAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Text;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public class SpanBasedMqttPacketBodyReader : IMqttPacketBodyReader
|
||||
{
|
||||
private ReadOnlyMemory<byte> _buffer;
|
||||
|
||||
private int _offset;
|
||||
|
||||
public void SetBuffer(ReadOnlyMemory<byte> buffer)
|
||||
{
|
||||
_buffer = buffer;
|
||||
_offset = 0;
|
||||
}
|
||||
|
||||
public int Length => _buffer.Length;
|
||||
|
||||
public bool EndOfStream => _buffer.Length.Equals(_offset);
|
||||
|
||||
public int Offset => _offset;
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
return _buffer.Span[_offset++];
|
||||
}
|
||||
|
||||
public byte[] ReadRemainingData()
|
||||
{
|
||||
return _buffer.Slice(_offset).ToArray();
|
||||
}
|
||||
|
||||
public byte[] ReadWithLengthPrefix()
|
||||
{
|
||||
return ReadSegmentWithLengthPrefix().ToArray();
|
||||
}
|
||||
|
||||
private ReadOnlySpan<byte> ReadSegmentWithLengthPrefix()
|
||||
{
|
||||
var span = _buffer.Span;
|
||||
var length = BinaryPrimitives.ReadUInt16BigEndian(span.Slice(_offset));
|
||||
|
||||
var result = span.Slice(_offset+2, length);
|
||||
_offset += 2 + length;
|
||||
return result;
|
||||
}
|
||||
|
||||
public unsafe string ReadStringWithLengthPrefix()
|
||||
{
|
||||
var buffer = ReadSegmentWithLengthPrefix();
|
||||
if (buffer.Length == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
fixed (byte* bytes = &buffer.GetPinnableReference())
|
||||
{
|
||||
var result = Encoding.UTF8.GetString(bytes, buffer.Length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public ushort ReadTwoByteInteger()
|
||||
{
|
||||
var result = BinaryPrimitives.ReadUInt16BigEndian(_buffer.Span.Slice(_offset));
|
||||
_offset += 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
public uint ReadFourByteInteger()
|
||||
{
|
||||
var result = BinaryPrimitives.ReadUInt32BigEndian(_buffer.Span.Slice(_offset));
|
||||
_offset += 4;
|
||||
return result;
|
||||
}
|
||||
|
||||
public uint ReadVariableLengthInteger()
|
||||
{
|
||||
var multiplier = 1;
|
||||
var value = 0U;
|
||||
byte encodedByte;
|
||||
|
||||
do
|
||||
{
|
||||
encodedByte = ReadByte();
|
||||
value += (uint)((encodedByte & 127) * multiplier);
|
||||
|
||||
if (multiplier > 2097152)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Variable length integer is invalid.");
|
||||
}
|
||||
|
||||
multiplier *= 128;
|
||||
} while ((encodedByte & 128) != 0);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public bool ReadBoolean()
|
||||
{
|
||||
var buffer = ReadByte();
|
||||
|
||||
if (buffer == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (buffer == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new MqttProtocolViolationException("Boolean values can be 0 or 1 only.");
|
||||
}
|
||||
|
||||
public void Seek(int position)
|
||||
{
|
||||
_offset = position;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
using MQTTnet.Formatter;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Buffers.Binary;
|
||||
using System.Text;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public class SpanBasedMqttPacketWriter : IMqttPacketWriter
|
||||
{
|
||||
private readonly ArrayPool<byte> _pool;
|
||||
|
||||
public SpanBasedMqttPacketWriter()
|
||||
{
|
||||
_pool = ArrayPool<byte>.Create();
|
||||
|
||||
}
|
||||
|
||||
private byte[] _buffer;
|
||||
private int _position;
|
||||
|
||||
public int Length { get; set; }
|
||||
|
||||
public void FreeBuffer()
|
||||
{
|
||||
_pool.Return(_buffer);
|
||||
}
|
||||
|
||||
public byte[] GetBuffer()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
public void Reset(int v)
|
||||
{
|
||||
_buffer = _pool.Rent(1500);
|
||||
Length = v;
|
||||
_position = v;
|
||||
}
|
||||
|
||||
public void Seek(int v)
|
||||
{
|
||||
_position = v;
|
||||
}
|
||||
|
||||
public void Write(byte value)
|
||||
{
|
||||
GrowIfNeeded(1);
|
||||
_buffer[_position] = value;
|
||||
Commit(1);
|
||||
}
|
||||
|
||||
public void Write(ushort value)
|
||||
{
|
||||
GrowIfNeeded(2);
|
||||
|
||||
BinaryPrimitives.WriteUInt16BigEndian(_buffer.AsSpan(_position), value);
|
||||
Commit(2);
|
||||
}
|
||||
|
||||
public void Write(IMqttPacketWriter propertyWriter)
|
||||
{
|
||||
if (propertyWriter == null) throw new ArgumentNullException(nameof(propertyWriter));
|
||||
|
||||
GrowIfNeeded(propertyWriter.Length);
|
||||
Write(propertyWriter.GetBuffer(), 0, propertyWriter.Length);
|
||||
}
|
||||
|
||||
public void Write(byte[] payload, int start, int length)
|
||||
{
|
||||
GrowIfNeeded(length);
|
||||
|
||||
payload.AsSpan(start, length).CopyTo(_buffer.AsSpan(_position));
|
||||
Commit(length);
|
||||
}
|
||||
|
||||
public void WriteVariableLengthInteger(uint value)
|
||||
{
|
||||
GrowIfNeeded(4);
|
||||
|
||||
var x = value;
|
||||
do
|
||||
{
|
||||
var encodedByte = x % 128;
|
||||
x = x / 128;
|
||||
if (x > 0)
|
||||
{
|
||||
encodedByte = encodedByte | 128;
|
||||
}
|
||||
|
||||
_buffer[_position] = (byte)encodedByte;
|
||||
Commit(1);
|
||||
} while (x > 0);
|
||||
}
|
||||
|
||||
public void WriteWithLengthPrefix(string value)
|
||||
{
|
||||
var bytesLength = Encoding.UTF8.GetByteCount(value ?? string.Empty);
|
||||
GrowIfNeeded(bytesLength + 2);
|
||||
|
||||
Write((ushort)bytesLength);
|
||||
Encoding.UTF8.GetBytes(value ?? string.Empty, 0, value?.Length ?? 0, _buffer, _position);
|
||||
Commit(bytesLength);
|
||||
}
|
||||
|
||||
public void WriteWithLengthPrefix(byte[] payload)
|
||||
{
|
||||
GrowIfNeeded(payload.Length + 2);
|
||||
|
||||
Write((ushort)payload.Length);
|
||||
payload.CopyTo(_buffer, _position);
|
||||
Commit(payload.Length);
|
||||
}
|
||||
|
||||
private void Commit(int count)
|
||||
{
|
||||
if (_position == Length)
|
||||
{
|
||||
Length += count;
|
||||
}
|
||||
|
||||
_position += count;
|
||||
}
|
||||
|
||||
private void GrowIfNeeded(int requiredAdditional)
|
||||
{
|
||||
var requiredTotal = _position + requiredAdditional;
|
||||
if (_buffer.Length >= requiredTotal)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var newBufferLength = _buffer.Length;
|
||||
while (newBufferLength < requiredTotal)
|
||||
{
|
||||
newBufferLength *= 2;
|
||||
}
|
||||
|
||||
var newBuffer = _pool.Rent(newBufferLength);
|
||||
Array.Copy(_buffer, newBuffer, _buffer.Length);
|
||||
_pool.Return(_buffer);
|
||||
_buffer = newBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class ApplicationMessageProcessedHandlerDelegate : IApplicationMessageProcessedHandler
|
||||
{
|
||||
private readonly Func<ApplicationMessageProcessedEventArgs, Task> _handler;
|
||||
|
||||
public ApplicationMessageProcessedHandlerDelegate(Action<ApplicationMessageProcessedEventArgs> handler)
|
||||
{
|
||||
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
||||
|
||||
_handler = context =>
|
||||
{
|
||||
handler(context);
|
||||
return Task.FromResult(0);
|
||||
};
|
||||
}
|
||||
|
||||
public ApplicationMessageProcessedHandlerDelegate(Func<ApplicationMessageProcessedEventArgs, Task> handler)
|
||||
{
|
||||
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
public Task HandleApplicationMessageProcessedAsync(ApplicationMessageProcessedEventArgs eventArgs)
|
||||
{
|
||||
return _handler(eventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class ApplicationMessageSkippedHandlerDelegate : IApplicationMessageSkippedHandler
|
||||
{
|
||||
private readonly Func<ApplicationMessageSkippedEventArgs, Task> _handler;
|
||||
|
||||
public ApplicationMessageSkippedHandlerDelegate(Action<ApplicationMessageSkippedEventArgs> handler)
|
||||
{
|
||||
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
||||
|
||||
_handler = eventArgs =>
|
||||
{
|
||||
handler(eventArgs);
|
||||
return Task.FromResult(0);
|
||||
};
|
||||
}
|
||||
|
||||
public ApplicationMessageSkippedHandlerDelegate(Func<ApplicationMessageSkippedEventArgs, Task> handler)
|
||||
{
|
||||
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
public Task HandleApplicationMessageSkippedAsync(ApplicationMessageSkippedEventArgs eventArgs)
|
||||
{
|
||||
return _handler(eventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class ConnectingFailedHandlerDelegate : IConnectingFailedHandler
|
||||
{
|
||||
private readonly Func<ManagedProcessFailedEventArgs, Task> _handler;
|
||||
|
||||
public ConnectingFailedHandlerDelegate(Action<ManagedProcessFailedEventArgs> handler)
|
||||
{
|
||||
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
||||
|
||||
_handler = eventArgs =>
|
||||
{
|
||||
handler(eventArgs);
|
||||
return Task.FromResult(0);
|
||||
};
|
||||
}
|
||||
|
||||
public ConnectingFailedHandlerDelegate(Func<ManagedProcessFailedEventArgs, Task> handler)
|
||||
{
|
||||
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
public Task HandleConnectingFailedAsync(ManagedProcessFailedEventArgs eventArgs)
|
||||
{
|
||||
return _handler(eventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public interface IApplicationMessageProcessedHandler
|
||||
{
|
||||
Task HandleApplicationMessageProcessedAsync(ApplicationMessageProcessedEventArgs eventArgs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public interface IApplicationMessageSkippedHandler
|
||||
{
|
||||
Task HandleApplicationMessageSkippedAsync(ApplicationMessageSkippedEventArgs eventArgs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public interface IConnectingFailedHandler
|
||||
{
|
||||
Task HandleConnectingFailedAsync(ManagedProcessFailedEventArgs eventArgs);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Disconnecting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
@@ -13,19 +14,20 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
int PendingApplicationMessagesCount { get; }
|
||||
IManagedMqttClientOptions Options { get; }
|
||||
|
||||
event EventHandler<MqttClientConnectedEventArgs> Connected;
|
||||
event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
|
||||
IMqttClientConnectedHandler ConnectedHandler { get; set; }
|
||||
IMqttClientDisconnectedHandler DisconnectedHandler { get; set; }
|
||||
|
||||
event EventHandler<ApplicationMessageProcessedEventArgs> ApplicationMessageProcessed;
|
||||
event EventHandler<ApplicationMessageSkippedEventArgs> ApplicationMessageSkipped;
|
||||
IApplicationMessageProcessedHandler ApplicationMessageProcessedHandler { get; set; }
|
||||
IApplicationMessageSkippedHandler ApplicationMessageSkippedHandler { get; set; }
|
||||
|
||||
IConnectingFailedHandler ConnectingFailedHandler { get; set; }
|
||||
ISynchronizingSubscriptionsFailedHandler SynchronizingSubscriptionsFailedHandler { get; set; }
|
||||
|
||||
event EventHandler<MqttManagedProcessFailedEventArgs> ConnectingFailed;
|
||||
event EventHandler<MqttManagedProcessFailedEventArgs> SynchronizingSubscriptionsFailed;
|
||||
|
||||
Task StartAsync(IManagedMqttClientOptions options);
|
||||
Task StopAsync();
|
||||
Task PingAsync(CancellationToken cancellationToken);
|
||||
|
||||
Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters);
|
||||
Task SubscribeAsync(IEnumerable<MqttTopicFilter> topicFilters);
|
||||
Task UnsubscribeAsync(IEnumerable<string> topics);
|
||||
|
||||
Task PublishAsync(ManagedMqttApplicationMessage applicationMessages);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Server;
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public interface ISynchronizingSubscriptionsFailedHandler
|
||||
{
|
||||
Task HandleSynchronizingSubscriptionsFailedAsync(ManagedProcessFailedEventArgs eventArgs);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core'">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' AND '$(SolutionName)' != 'MQTTnet.noUWP' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
<Product />
|
||||
<Company />
|
||||
<Authors />
|
||||
@@ -14,7 +14,7 @@
|
||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||
<NugetTargetMoniker>UAP,Version=v10.0</NugetTargetMoniker>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion>10.0.17134.0</TargetPlatformVersion>
|
||||
<TargetPlatformVersion>10.0.18362.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
|
||||
<TargetFrameworkIdentifier>.NETCore</TargetFrameworkIdentifier>
|
||||
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
|
||||
|
||||
@@ -1,47 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Disconnecting;
|
||||
using MQTTnet.Client.Publishing;
|
||||
using MQTTnet.Client.Receiving;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
using MqttClientConnectedEventArgs = MQTTnet.Client.Connecting.MqttClientConnectedEventArgs;
|
||||
using MqttClientDisconnectedEventArgs = MQTTnet.Client.Disconnecting.MqttClientDisconnectedEventArgs;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class ManagedMqttClient : IManagedMqttClient
|
||||
public class ManagedMqttClient : Disposable, IManagedMqttClient
|
||||
{
|
||||
private readonly BlockingQueue<ManagedMqttApplicationMessage> _messageQueue = new BlockingQueue<ManagedMqttApplicationMessage>();
|
||||
|
||||
/// <summary>
|
||||
/// The subscriptions are managed in 2 separate buckets:
|
||||
/// <see cref="_subscriptions"/> and <see cref="_unsubscriptions"/> are processed during normal operation
|
||||
/// and are moved to the <see cref="_reconnectSubscriptions"/> when they get processed. They can be accessed by
|
||||
/// any thread and are therefore mutex'ed. <see cref="_reconnectSubscriptions"/> get sent to the broker
|
||||
/// at reconnect and are solely owned by <see cref="MaintainConnectionAsync"/>.
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, MqttQualityOfServiceLevel> _reconnectSubscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
|
||||
private readonly Dictionary<string, MqttQualityOfServiceLevel> _subscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
|
||||
private readonly HashSet<string> _unsubscriptions = new HashSet<string>();
|
||||
private readonly SemaphoreSlim _subscriptionsQueuedSignal = new SemaphoreSlim(0);
|
||||
|
||||
private readonly IMqttClient _mqttClient;
|
||||
private readonly IMqttNetChildLogger _logger;
|
||||
private readonly IMqttNetScopedLogger _logger;
|
||||
|
||||
private readonly AsyncLock _messageQueueLock = new AsyncLock();
|
||||
|
||||
private CancellationTokenSource _connectionCancellationToken;
|
||||
private CancellationTokenSource _publishingCancellationToken;
|
||||
private Task _maintainConnectionTask;
|
||||
|
||||
private ManagedMqttClientStorageManager _storageManager;
|
||||
|
||||
private bool _subscriptionsNotPushed;
|
||||
|
||||
public ManagedMqttClient(IMqttClient mqttClient, IMqttNetChildLogger logger)
|
||||
public ManagedMqttClient(IMqttClient mqttClient, IMqttNetLogger logger)
|
||||
{
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
|
||||
|
||||
_mqttClient.Connected += OnConnected;
|
||||
_mqttClient.Disconnected += OnDisconnected;
|
||||
_mqttClient.ApplicationMessageReceived += OnApplicationMessageReceived;
|
||||
|
||||
_logger = logger.CreateChildLogger(nameof(ManagedMqttClient));
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
_logger = logger.CreateScopedLogger(nameof(ManagedMqttClient));
|
||||
}
|
||||
|
||||
public bool IsConnected => _mqttClient.IsConnected;
|
||||
@@ -49,27 +56,41 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
public int PendingApplicationMessagesCount => _messageQueue.Count;
|
||||
public IManagedMqttClientOptions Options { get; private set; }
|
||||
|
||||
public event EventHandler<MqttClientConnectedEventArgs> Connected;
|
||||
public event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
|
||||
public IMqttClientConnectedHandler ConnectedHandler
|
||||
{
|
||||
get => _mqttClient.ConnectedHandler;
|
||||
set => _mqttClient.ConnectedHandler = value;
|
||||
}
|
||||
|
||||
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
||||
public event EventHandler<ApplicationMessageProcessedEventArgs> ApplicationMessageProcessed;
|
||||
public event EventHandler<ApplicationMessageSkippedEventArgs> ApplicationMessageSkipped;
|
||||
public IMqttClientDisconnectedHandler DisconnectedHandler
|
||||
{
|
||||
get => _mqttClient.DisconnectedHandler;
|
||||
set => _mqttClient.DisconnectedHandler = value;
|
||||
}
|
||||
|
||||
public event EventHandler<MqttManagedProcessFailedEventArgs> ConnectingFailed;
|
||||
public event EventHandler<MqttManagedProcessFailedEventArgs> SynchronizingSubscriptionsFailed;
|
||||
public IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler
|
||||
{
|
||||
get => _mqttClient.ApplicationMessageReceivedHandler;
|
||||
set => _mqttClient.ApplicationMessageReceivedHandler = value;
|
||||
}
|
||||
|
||||
public IApplicationMessageProcessedHandler ApplicationMessageProcessedHandler { get; set; }
|
||||
|
||||
public IApplicationMessageSkippedHandler ApplicationMessageSkippedHandler { get; set; }
|
||||
|
||||
public IConnectingFailedHandler ConnectingFailedHandler { get; set; }
|
||||
|
||||
public ISynchronizingSubscriptionsFailedHandler SynchronizingSubscriptionsFailedHandler { get; set; }
|
||||
|
||||
public async Task StartAsync(IManagedMqttClientOptions options)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
if (options.ClientOptions == null) throw new ArgumentException("The client options are not set.", nameof(options));
|
||||
|
||||
if (!options.ClientOptions.CleanSession)
|
||||
{
|
||||
throw new NotSupportedException("The managed client does not support existing sessions.");
|
||||
}
|
||||
|
||||
if (_connectionCancellationToken != null) throw new InvalidOperationException("The managed client is already started.");
|
||||
if (!_maintainConnectionTask?.IsCompleted ?? false) throw new InvalidOperationException("The managed client is already started.");
|
||||
|
||||
Options = options;
|
||||
|
||||
@@ -86,25 +107,37 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
_connectionCancellationToken = new CancellationTokenSource();
|
||||
|
||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
Task.Run(() => MaintainConnectionAsync(_connectionCancellationToken.Token), _connectionCancellationToken.Token);
|
||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
_maintainConnectionTask = Task.Run(() => MaintainConnectionAsync(_connectionCancellationToken.Token), _connectionCancellationToken.Token);
|
||||
_maintainConnectionTask.Forget(_logger);
|
||||
|
||||
_logger.Info("Started");
|
||||
}
|
||||
|
||||
public Task StopAsync()
|
||||
public async Task StopAsync()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
StopPublishing();
|
||||
StopMaintainingConnection();
|
||||
|
||||
_messageQueue.Clear();
|
||||
|
||||
return Task.FromResult(0);
|
||||
if (_maintainConnectionTask != null)
|
||||
{
|
||||
await Task.WhenAny(_maintainConnectionTask);
|
||||
_maintainConnectionTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage)
|
||||
public Task PingAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return _mqttClient.PingAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
await PublishAsync(new ManagedMqttApplicationMessageBuilder().WithApplicationMessage(applicationMessage).Build()).ConfigureAwait(false);
|
||||
@@ -113,44 +146,68 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
public async Task PublishAsync(ManagedMqttApplicationMessage applicationMessage)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
if (Options == null) throw new InvalidOperationException("call StartAsync before publishing messages");
|
||||
|
||||
MqttTopicValidator.ThrowIfInvalid(applicationMessage.ApplicationMessage.Topic);
|
||||
|
||||
ManagedMqttApplicationMessage removedMessage = null;
|
||||
lock (_messageQueue)
|
||||
ApplicationMessageSkippedEventArgs applicationMessageSkippedEventArgs = null;
|
||||
|
||||
try
|
||||
{
|
||||
if (_messageQueue.Count >= Options.MaxPendingMessages)
|
||||
using (await _messageQueueLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
||||
{
|
||||
if (Options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropNewMessage)
|
||||
if (_messageQueue.Count >= Options.MaxPendingMessages)
|
||||
{
|
||||
_logger.Verbose("Skipping publish of new application message because internal queue is full.");
|
||||
ApplicationMessageSkipped?.Invoke(this, new ApplicationMessageSkippedEventArgs(applicationMessage));
|
||||
return;
|
||||
if (Options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropNewMessage)
|
||||
{
|
||||
_logger.Verbose("Skipping publish of new application message because internal queue is full.");
|
||||
applicationMessageSkippedEventArgs = new ApplicationMessageSkippedEventArgs(applicationMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage)
|
||||
{
|
||||
removedMessage = _messageQueue.RemoveFirst();
|
||||
_logger.Verbose("Removed oldest application message from internal queue because it is full.");
|
||||
applicationMessageSkippedEventArgs = new ApplicationMessageSkippedEventArgs(removedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
if (Options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage)
|
||||
_messageQueue.Enqueue(applicationMessage);
|
||||
|
||||
if (_storageManager != null)
|
||||
{
|
||||
removedMessage = _messageQueue.RemoveFirst();
|
||||
_logger.Verbose("Removed oldest application message from internal queue because it is full.");
|
||||
ApplicationMessageSkipped?.Invoke(this, new ApplicationMessageSkippedEventArgs(removedMessage));
|
||||
if (removedMessage != null)
|
||||
{
|
||||
await _storageManager.RemoveAsync(removedMessage).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await _storageManager.AddAsync(applicationMessage).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
_messageQueue.Enqueue(applicationMessage);
|
||||
}
|
||||
|
||||
if (_storageManager != null)
|
||||
finally
|
||||
{
|
||||
if (removedMessage != null)
|
||||
if (applicationMessageSkippedEventArgs != null)
|
||||
{
|
||||
await _storageManager.RemoveAsync(removedMessage).ConfigureAwait(false);
|
||||
var applicationMessageSkippedHandler = ApplicationMessageSkippedHandler;
|
||||
if (applicationMessageSkippedHandler != null)
|
||||
{
|
||||
await applicationMessageSkippedHandler.HandleApplicationMessageSkippedAsync(applicationMessageSkippedEventArgs).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
await _storageManager.AddAsync(applicationMessage).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
|
||||
public Task SubscribeAsync(IEnumerable<MqttTopicFilter> topicFilters)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
||||
|
||||
lock (_subscriptions)
|
||||
@@ -158,36 +215,52 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
foreach (var topicFilter in topicFilters)
|
||||
{
|
||||
_subscriptions[topicFilter.Topic] = topicFilter.QualityOfServiceLevel;
|
||||
_subscriptionsNotPushed = true;
|
||||
_unsubscriptions.Remove(topicFilter.Topic);
|
||||
}
|
||||
}
|
||||
_subscriptionsQueuedSignal.Release();
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task UnsubscribeAsync(IEnumerable<string> topics)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (topics == null) throw new ArgumentNullException(nameof(topics));
|
||||
|
||||
lock (_subscriptions)
|
||||
{
|
||||
foreach (var topic in topics)
|
||||
{
|
||||
if (_subscriptions.Remove(topic))
|
||||
{
|
||||
_unsubscriptions.Add(topic);
|
||||
_subscriptionsNotPushed = true;
|
||||
}
|
||||
_subscriptions.Remove(topic);
|
||||
_unsubscriptions.Add(topic);
|
||||
}
|
||||
}
|
||||
_subscriptionsQueuedSignal.Release();
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
_connectionCancellationToken?.Dispose();
|
||||
_publishingCancellationToken?.Dispose();
|
||||
if (disposing)
|
||||
{
|
||||
StopPublishing();
|
||||
StopMaintainingConnection();
|
||||
|
||||
if (_maintainConnectionTask != null)
|
||||
{
|
||||
_maintainConnectionTask.GetAwaiter().GetResult();
|
||||
_maintainConnectionTask = null;
|
||||
}
|
||||
|
||||
_messageQueue.Dispose();
|
||||
_messageQueueLock.Dispose();
|
||||
_mqttClient.Dispose();
|
||||
_subscriptionsQueuedSignal.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private async Task MaintainConnectionAsync(CancellationToken cancellationToken)
|
||||
@@ -204,20 +277,29 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error(exception, "Unhandled exception while maintaining connection.");
|
||||
_logger.Error(exception, "Error exception while maintaining connection.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
if (!IsDisposed)
|
||||
{
|
||||
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error(exception, "Error while disconnecting.");
|
||||
}
|
||||
try
|
||||
{
|
||||
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error(exception, "Error while disconnecting.");
|
||||
}
|
||||
|
||||
_logger.Info("Stopped");
|
||||
_logger.Info("Stopped");
|
||||
}
|
||||
_reconnectSubscriptions.Clear();
|
||||
lock (_subscriptions)
|
||||
{
|
||||
_subscriptions.Clear();
|
||||
_unsubscriptions.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,16 +315,22 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
return;
|
||||
}
|
||||
|
||||
if (connectionState == ReconnectionResult.Reconnected || _subscriptionsNotPushed)
|
||||
if (connectionState == ReconnectionResult.Reconnected)
|
||||
{
|
||||
await PublishReconnectSubscriptionsAsync().ConfigureAwait(false);
|
||||
StartPublishing();
|
||||
return;
|
||||
}
|
||||
|
||||
if (connectionState == ReconnectionResult.Recovered)
|
||||
{
|
||||
await SynchronizeSubscriptionsAsync().ConfigureAwait(false);
|
||||
StartPublishing();
|
||||
return;
|
||||
}
|
||||
|
||||
if (connectionState == ReconnectionResult.StillConnected)
|
||||
{
|
||||
await Task.Delay(Options.ConnectionCheckInterval, cancellationToken).ConfigureAwait(false);
|
||||
await PublishSubscriptionsAsync(Options.ConnectionCheckInterval, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
@@ -250,29 +338,29 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
}
|
||||
catch (MqttCommunicationException exception)
|
||||
{
|
||||
_logger.Warning(exception, "Communication exception while maintaining connection.");
|
||||
_logger.Warning(exception, "Communication error while maintaining connection.");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error(exception, "Unhandled exception while maintaining connection.");
|
||||
_logger.Error(exception, "Error exception while maintaining connection.");
|
||||
}
|
||||
}
|
||||
|
||||
private void PublishQueuedMessages(CancellationToken cancellationToken)
|
||||
private async Task PublishQueuedMessagesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested && _mqttClient.IsConnected)
|
||||
{
|
||||
//Peek at the message without dequeueing in order to prevent the
|
||||
//possibility of the queue growing beyond the configured cap.
|
||||
//Previously, messages could be re-enqueued if there was an
|
||||
//exception, and this re-enqueueing did not honor the cap.
|
||||
//Furthermore, because re-enqueueing would shuffle the order
|
||||
//of the messages, the DropOldestQueuedMessage strategy would
|
||||
//be unable to know which message is actually the oldest and would
|
||||
//instead drop the first item in the queue.
|
||||
var message = _messageQueue.PeekAndWait();
|
||||
// Peek at the message without dequeueing in order to prevent the
|
||||
// possibility of the queue growing beyond the configured cap.
|
||||
// Previously, messages could be re-enqueued if there was an
|
||||
// exception, and this re-enqueueing did not honor the cap.
|
||||
// Furthermore, because re-enqueueing would shuffle the order
|
||||
// of the messages, the DropOldestQueuedMessage strategy would
|
||||
// be unable to know which message is actually the oldest and would
|
||||
// instead drop the first item in the queue.
|
||||
var message = _messageQueue.PeekAndWait(cancellationToken);
|
||||
if (message == null)
|
||||
{
|
||||
continue;
|
||||
@@ -280,7 +368,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
TryPublishQueuedMessage(message);
|
||||
await TryPublishQueuedMessageAsync(message).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
@@ -288,7 +376,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error(exception, "Unhandled exception while publishing queued application messages.");
|
||||
_logger.Error(exception, "Error while publishing queued application messages.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -296,23 +384,28 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
}
|
||||
}
|
||||
|
||||
private void TryPublishQueuedMessage(ManagedMqttApplicationMessage message)
|
||||
private async Task TryPublishQueuedMessageAsync(ManagedMqttApplicationMessage message)
|
||||
{
|
||||
Exception transmitException = null;
|
||||
try
|
||||
{
|
||||
_mqttClient.PublishAsync(message.ApplicationMessage).GetAwaiter().GetResult();
|
||||
lock (_messageQueue) //lock to avoid conflict with this.PublishAsync
|
||||
await _mqttClient.PublishAsync(message.ApplicationMessage).ConfigureAwait(false);
|
||||
|
||||
using (await _messageQueueLock.WaitAsync(CancellationToken.None).ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
|
||||
{
|
||||
//While publishing this message, this.PublishAsync could have booted this
|
||||
//message off the queue to make room for another (when using a cap
|
||||
//with the DropOldestQueuedMessage strategy). If the first item
|
||||
//in the queue is equal to this message, then it's safe to remove
|
||||
//it from the queue. If not, that means this.PublishAsync has already
|
||||
//removed it, in which case we don't want to do anything.
|
||||
// While publishing this message, this.PublishAsync could have booted this
|
||||
// message off the queue to make room for another (when using a cap
|
||||
// with the DropOldestQueuedMessage strategy). If the first item
|
||||
// in the queue is equal to this message, then it's safe to remove
|
||||
// it from the queue. If not, that means this.PublishAsync has already
|
||||
// removed it, in which case we don't want to do anything.
|
||||
_messageQueue.RemoveFirst(i => i.Id.Equals(message.Id));
|
||||
|
||||
if (_storageManager != null)
|
||||
{
|
||||
await _storageManager.RemoveAsync(message).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
_storageManager?.RemoveAsync(message).GetAwaiter().GetResult();
|
||||
}
|
||||
catch (MqttCommunicationException exception)
|
||||
{
|
||||
@@ -329,63 +422,111 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
//contradict the expected behavior of QoS 1 and 2, that's also true
|
||||
//for the usage of a message queue cap, so it's still consistent
|
||||
//with prior behavior in that way.
|
||||
lock (_messageQueue) //lock to avoid conflict with this.PublishAsync
|
||||
using (await _messageQueueLock.WaitAsync(CancellationToken.None).ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
|
||||
{
|
||||
_messageQueue.RemoveFirst(i => i.Id.Equals(message.Id));
|
||||
|
||||
if (_storageManager != null)
|
||||
{
|
||||
await _storageManager.RemoveAsync(message).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
transmitException = exception;
|
||||
_logger.Error(exception, $"Unhandled exception while publishing application message ({message.Id}).");
|
||||
_logger.Error(exception, $"Error while publishing application message ({message.Id}).");
|
||||
}
|
||||
finally
|
||||
{
|
||||
ApplicationMessageProcessed?.Invoke(this, new ApplicationMessageProcessedEventArgs(message, transmitException));
|
||||
var eventHandler = ApplicationMessageProcessedHandler;
|
||||
if (eventHandler != null)
|
||||
{
|
||||
var eventArguments = new ApplicationMessageProcessedEventArgs(message, transmitException);
|
||||
await eventHandler.HandleApplicationMessageProcessedAsync(eventArguments).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SynchronizeSubscriptionsAsync()
|
||||
private async Task PublishSubscriptionsAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.Info(nameof(ManagedMqttClient), "Synchronizing subscriptions");
|
||||
|
||||
List<TopicFilter> subscriptions;
|
||||
HashSet<string> unsubscriptions;
|
||||
|
||||
lock (_subscriptions)
|
||||
var endTime = DateTime.UtcNow + timeout;
|
||||
while (await _subscriptionsQueuedSignal.WaitAsync(GetRemainingTime(endTime), cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
subscriptions = _subscriptions.Select(i => new TopicFilter { Topic = i.Key, QualityOfServiceLevel = i.Value }).ToList();
|
||||
List<MqttTopicFilter> subscriptions;
|
||||
HashSet<string> unsubscriptions;
|
||||
|
||||
unsubscriptions = new HashSet<string>(_unsubscriptions);
|
||||
_unsubscriptions.Clear();
|
||||
lock (_subscriptions)
|
||||
{
|
||||
subscriptions = _subscriptions.Select(i => new MqttTopicFilter { Topic = i.Key, QualityOfServiceLevel = i.Value }).ToList();
|
||||
_subscriptions.Clear();
|
||||
unsubscriptions = new HashSet<string>(_unsubscriptions);
|
||||
_unsubscriptions.Clear();
|
||||
}
|
||||
|
||||
_subscriptionsNotPushed = false;
|
||||
if (!subscriptions.Any() && !unsubscriptions.Any())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_logger.Verbose($"Publishing subscriptions ({subscriptions.Count} subscriptions and {unsubscriptions.Count} unsubscriptions)");
|
||||
|
||||
foreach (var unsubscription in unsubscriptions)
|
||||
{
|
||||
_reconnectSubscriptions.Remove(unsubscription);
|
||||
}
|
||||
|
||||
foreach (var subscription in subscriptions)
|
||||
{
|
||||
_reconnectSubscriptions[subscription.Topic] = subscription.QualityOfServiceLevel;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (unsubscriptions.Any())
|
||||
{
|
||||
await _mqttClient.UnsubscribeAsync(unsubscriptions.ToArray()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (subscriptions.Any())
|
||||
{
|
||||
await _mqttClient.SubscribeAsync(subscriptions.ToArray()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
await HandleSubscriptionExceptionAsync(exception).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!subscriptions.Any() && !unsubscriptions.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
private async Task PublishReconnectSubscriptionsAsync()
|
||||
{
|
||||
_logger.Info("Publishing subscriptions at reconnect");
|
||||
|
||||
try
|
||||
{
|
||||
if (unsubscriptions.Any())
|
||||
if (_reconnectSubscriptions.Any())
|
||||
{
|
||||
await _mqttClient.UnsubscribeAsync(unsubscriptions).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (subscriptions.Any())
|
||||
{
|
||||
await _mqttClient.SubscribeAsync(subscriptions).ConfigureAwait(false);
|
||||
var subscriptions = _reconnectSubscriptions.Select(i => new MqttTopicFilter { Topic = i.Key, QualityOfServiceLevel = i.Value });
|
||||
await _mqttClient.SubscribeAsync(subscriptions.ToArray()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Warning(exception, "Synchronizing subscriptions failed.");
|
||||
_subscriptionsNotPushed = true;
|
||||
await HandleSubscriptionExceptionAsync(exception).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
SynchronizingSubscriptionsFailed?.Invoke(this, new MqttManagedProcessFailedEventArgs(exception));
|
||||
private async Task HandleSubscriptionExceptionAsync(Exception exception)
|
||||
{
|
||||
_logger.Warning(exception, "Synchronizing subscriptions failed.");
|
||||
|
||||
var synchronizingSubscriptionsFailedHandler = SynchronizingSubscriptionsFailedHandler;
|
||||
if (SynchronizingSubscriptionsFailedHandler != null)
|
||||
{
|
||||
await synchronizingSubscriptionsFailedHandler.HandleSynchronizingSubscriptionsFailedAsync(new ManagedProcessFailedEventArgs(exception)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,31 +539,21 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
try
|
||||
{
|
||||
await _mqttClient.ConnectAsync(Options.ClientOptions).ConfigureAwait(false);
|
||||
return ReconnectionResult.Reconnected;
|
||||
var result = await _mqttClient.ConnectAsync(Options.ClientOptions).ConfigureAwait(false);
|
||||
return result.IsSessionPresent ? ReconnectionResult.Recovered : ReconnectionResult.Reconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
ConnectingFailed?.Invoke(this, new MqttManagedProcessFailedEventArgs(exception));
|
||||
var connectingFailedHandler = ConnectingFailedHandler;
|
||||
if (connectingFailedHandler != null)
|
||||
{
|
||||
await connectingFailedHandler.HandleConnectingFailedAsync(new ManagedProcessFailedEventArgs(exception)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return ReconnectionResult.NotConnected;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
{
|
||||
ApplicationMessageReceived?.Invoke(this, eventArgs);
|
||||
}
|
||||
|
||||
private void OnDisconnected(object sender, MqttClientDisconnectedEventArgs eventArgs)
|
||||
{
|
||||
Disconnected?.Invoke(this, eventArgs);
|
||||
}
|
||||
|
||||
private void OnConnected(object sender, MqttClientConnectedEventArgs eventArgs)
|
||||
{
|
||||
Connected?.Invoke(this, eventArgs);
|
||||
}
|
||||
|
||||
private void StartPublishing()
|
||||
{
|
||||
if (_publishingCancellationToken != null)
|
||||
@@ -433,7 +564,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
var cts = new CancellationTokenSource();
|
||||
_publishingCancellationToken = cts;
|
||||
|
||||
Task.Factory.StartNew(() => PublishQueuedMessages(cts.Token), cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
||||
Task.Run(() => PublishQueuedMessagesAsync(cts.Token), cts.Token).Forget(_logger);
|
||||
}
|
||||
|
||||
private void StopPublishing()
|
||||
@@ -449,5 +580,11 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
_connectionCancellationToken?.Dispose();
|
||||
_connectionCancellationToken = null;
|
||||
}
|
||||
|
||||
private TimeSpan GetRemainingTime(DateTime endTime)
|
||||
{
|
||||
var remainingTime = endTime - DateTime.UtcNow;
|
||||
return remainingTime < TimeSpan.Zero ? TimeSpan.Zero : remainingTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +1,229 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Disconnecting;
|
||||
using MQTTnet.Client.Publishing;
|
||||
using MQTTnet.Client.Receiving;
|
||||
using MQTTnet.Protocol;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public static class ManagedMqttClientExtensions
|
||||
{
|
||||
public static Task SubscribeAsync(this IManagedMqttClient managedClient, params TopicFilter[] topicFilters)
|
||||
public static IManagedMqttClient UseConnectedHandler(this IManagedMqttClient client, Func<MqttClientConnectedEventArgs, Task> handler)
|
||||
{
|
||||
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
return managedClient.SubscribeAsync(topicFilters);
|
||||
if (handler == null)
|
||||
{
|
||||
return client.UseConnectedHandler((IMqttClientConnectedHandler)null);
|
||||
}
|
||||
|
||||
return client.UseConnectedHandler(new MqttClientConnectedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static Task SubscribeAsync(this IManagedMqttClient managedClient, string topic, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
public static IManagedMqttClient UseConnectedHandler(this IManagedMqttClient client, Action<MqttClientConnectedEventArgs> handler)
|
||||
{
|
||||
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
return client.UseConnectedHandler((IMqttClientConnectedHandler)null);
|
||||
}
|
||||
|
||||
return client.UseConnectedHandler(new MqttClientConnectedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IManagedMqttClient UseConnectedHandler(this IManagedMqttClient client, IMqttClientConnectedHandler handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
client.ConnectedHandler = handler;
|
||||
return client;
|
||||
}
|
||||
|
||||
public static IManagedMqttClient UseDisconnectedHandler(this IManagedMqttClient client, Func<MqttClientDisconnectedEventArgs, Task> handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
return client.UseDisconnectedHandler((IMqttClientDisconnectedHandler)null);
|
||||
}
|
||||
|
||||
return client.UseDisconnectedHandler(new MqttClientDisconnectedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IManagedMqttClient UseDisconnectedHandler(this IManagedMqttClient client, Action<MqttClientDisconnectedEventArgs> handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
return client.UseDisconnectedHandler((IMqttClientDisconnectedHandler)null);
|
||||
}
|
||||
|
||||
return client.UseDisconnectedHandler(new MqttClientDisconnectedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IManagedMqttClient UseDisconnectedHandler(this IManagedMqttClient client, IMqttClientDisconnectedHandler handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
client.DisconnectedHandler = handler;
|
||||
return client;
|
||||
}
|
||||
|
||||
public static IManagedMqttClient UseApplicationMessageReceivedHandler(this IManagedMqttClient client, Func<MqttApplicationMessageReceivedEventArgs, Task> handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
return client.UseApplicationMessageReceivedHandler((IMqttApplicationMessageReceivedHandler)null);
|
||||
}
|
||||
|
||||
return client.UseApplicationMessageReceivedHandler(new MqttApplicationMessageReceivedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IManagedMqttClient UseApplicationMessageReceivedHandler(this IManagedMqttClient client, Action<MqttApplicationMessageReceivedEventArgs> handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
return client.UseApplicationMessageReceivedHandler((IMqttApplicationMessageReceivedHandler)null);
|
||||
}
|
||||
|
||||
return client.UseApplicationMessageReceivedHandler(new MqttApplicationMessageReceivedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IManagedMqttClient UseApplicationMessageReceivedHandler(this IManagedMqttClient client, IMqttApplicationMessageReceivedHandler handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
client.ApplicationMessageReceivedHandler = handler;
|
||||
return client;
|
||||
}
|
||||
|
||||
public static Task SubscribeAsync(this IManagedMqttClient client, params MqttTopicFilter[] topicFilters)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
return client.SubscribeAsync(topicFilters);
|
||||
}
|
||||
|
||||
public static Task SubscribeAsync(this IManagedMqttClient client, string topic, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return managedClient.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build());
|
||||
return client.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build());
|
||||
}
|
||||
|
||||
public static Task SubscribeAsync(this IManagedMqttClient managedClient, string topic)
|
||||
public static Task SubscribeAsync(this IManagedMqttClient client, string topic)
|
||||
{
|
||||
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return managedClient.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).Build());
|
||||
return client.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic(topic).Build());
|
||||
}
|
||||
|
||||
public static Task UnsubscribeAsync(this IManagedMqttClient managedClient, params string[] topicFilters)
|
||||
public static Task UnsubscribeAsync(this IManagedMqttClient client, params string[] topicFilters)
|
||||
{
|
||||
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
return managedClient.UnsubscribeAsync(topicFilters);
|
||||
return client.UnsubscribeAsync(topicFilters);
|
||||
}
|
||||
|
||||
public static async Task PublishAsync(this IManagedMqttClient client, IEnumerable<MqttApplicationMessage> applicationMessages)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
|
||||
|
||||
foreach (var applicationMessage in applicationMessages)
|
||||
{
|
||||
await client.PublishAsync(applicationMessage).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, MqttApplicationMessage applicationMessage)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
return client.PublishAsync(applicationMessage, CancellationToken.None);
|
||||
}
|
||||
|
||||
public static async Task PublishAsync(this IManagedMqttClient client, params MqttApplicationMessage[] applicationMessages)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
|
||||
|
||||
foreach (var applicationMessage in applicationMessages)
|
||||
{
|
||||
await client.PublishAsync(applicationMessage, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, string topic)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return client.PublishAsync(builder => builder
|
||||
.WithTopic(topic));
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, string topic, string payload)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return client.PublishAsync(builder => builder
|
||||
.WithTopic(topic)
|
||||
.WithPayload(payload));
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return client.PublishAsync(builder => builder
|
||||
.WithTopic(topic)
|
||||
.WithPayload(payload)
|
||||
.WithQualityOfServiceLevel(qualityOfServiceLevel));
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return client.PublishAsync(builder => builder
|
||||
.WithTopic(topic)
|
||||
.WithPayload(payload)
|
||||
.WithQualityOfServiceLevel(qualityOfServiceLevel)
|
||||
.WithRetainFlag(retain));
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, Func<MqttApplicationMessageBuilder, MqttApplicationMessageBuilder> builder, CancellationToken cancellationToken)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
var message = builder(new MqttApplicationMessageBuilder()).Build();
|
||||
return client.PublishAsync(message, cancellationToken);
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, Func<MqttApplicationMessageBuilder, MqttApplicationMessageBuilder> builder)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
var message = builder(new MqttApplicationMessageBuilder()).Build();
|
||||
return client.PublishAsync(message, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
using (await _messagesLock.LockAsync(CancellationToken.None).ConfigureAwait(false))
|
||||
using (await _messagesLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
||||
{
|
||||
_messages.Add(applicationMessage);
|
||||
await SaveAsync().ConfigureAwait(false);
|
||||
@@ -41,7 +41,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
using (await _messagesLock.LockAsync(CancellationToken.None).ConfigureAwait(false))
|
||||
using (await _messagesLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
||||
{
|
||||
var index = _messages.IndexOf(applicationMessage);
|
||||
if (index == -1)
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class ManagedProcessFailedEventArgs : EventArgs
|
||||
{
|
||||
public ManagedProcessFailedEventArgs(Exception exception)
|
||||
{
|
||||
Exception = exception ?? throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
public Exception Exception { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,23 @@
|
||||
using System;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Diagnostics;
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public static class MqttFactoryExtensions
|
||||
{
|
||||
public static IManagedMqttClient CreateManagedMqttClient(this IMqttClientFactory factory)
|
||||
public static IManagedMqttClient CreateManagedMqttClient(this IMqttFactory factory)
|
||||
{
|
||||
if (factory == null) throw new ArgumentNullException(nameof(factory));
|
||||
|
||||
return new ManagedMqttClient(factory.CreateMqttClient(), new MqttNetLogger().CreateChildLogger());
|
||||
return new ManagedMqttClient(factory.CreateMqttClient(), factory.DefaultLogger);
|
||||
}
|
||||
|
||||
public static IManagedMqttClient CreateManagedMqttClient(this IMqttClientFactory factory, IMqttNetLogger logger)
|
||||
public static IManagedMqttClient CreateManagedMqttClient(this IMqttFactory factory, IMqttNetLogger logger)
|
||||
{
|
||||
if (factory == null) throw new ArgumentNullException(nameof(factory));
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
return new ManagedMqttClient(factory.CreateMqttClient(), logger.CreateChildLogger());
|
||||
return new ManagedMqttClient(factory.CreateMqttClient(logger), logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class MqttManagedProcessFailedEventArgs : EventArgs
|
||||
{
|
||||
public MqttManagedProcessFailedEventArgs(Exception exception)
|
||||
{
|
||||
Exception = exception;
|
||||
}
|
||||
|
||||
public Exception Exception { get; }
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
{
|
||||
StillConnected,
|
||||
Reconnected,
|
||||
Recovered,
|
||||
NotConnected
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class SynchronizingSubscriptionsFailedHandlerDelegate : ISynchronizingSubscriptionsFailedHandler
|
||||
{
|
||||
private readonly Func<ManagedProcessFailedEventArgs, Task> _handler;
|
||||
|
||||
public SynchronizingSubscriptionsFailedHandlerDelegate(Action<ManagedProcessFailedEventArgs> handler)
|
||||
{
|
||||
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
||||
|
||||
_handler = context =>
|
||||
{
|
||||
handler(context);
|
||||
return Task.FromResult(0);
|
||||
};
|
||||
}
|
||||
|
||||
public SynchronizingSubscriptionsFailedHandlerDelegate(Func<ManagedProcessFailedEventArgs, Task> handler)
|
||||
{
|
||||
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
public Task HandleSynchronizingSubscriptionsFailedAsync(ManagedProcessFailedEventArgs eventArgs)
|
||||
{
|
||||
return _handler(eventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core'">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' AND '$(SolutionName)' != 'MQTTnet.noUWP' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
<Product />
|
||||
<Company />
|
||||
<Authors />
|
||||
@@ -14,7 +14,7 @@
|
||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||
<NugetTargetMoniker>UAP,Version=v10.0</NugetTargetMoniker>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion>10.0.17134.0</TargetPlatformVersion>
|
||||
<TargetPlatformVersion>10.0.18362.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
|
||||
<TargetFrameworkIdentifier>.NETCore</TargetFrameworkIdentifier>
|
||||
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
|
||||
|
||||
@@ -1,24 +1,38 @@
|
||||
using System;
|
||||
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;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Extensions.Rpc
|
||||
{
|
||||
public class MqttRpcClient : IDisposable
|
||||
public sealed class MqttRpcClient : IDisposable
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, TaskCompletionSource<byte[]>> _waitingCalls = new ConcurrentDictionary<string, TaskCompletionSource<byte[]>>();
|
||||
private readonly IMqttClient _mqttClient;
|
||||
private readonly IMqttRpcClientOptions _options;
|
||||
private readonly RpcAwareApplicationMessageReceivedHandler _applicationMessageReceivedHandler;
|
||||
|
||||
public MqttRpcClient(IMqttClient mqttClient)
|
||||
[Obsolete("Use MqttRpcClient(IMqttClient mqttClient, IMqttRpcClientOptions options).")]
|
||||
public MqttRpcClient(IMqttClient mqttClient) : this(mqttClient, new MqttRpcClientOptions())
|
||||
{
|
||||
}
|
||||
|
||||
public MqttRpcClient(IMqttClient mqttClient, IMqttRpcClientOptions options)
|
||||
{
|
||||
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
|
||||
_mqttClient.ApplicationMessageReceived += OnApplicationMessageReceived;
|
||||
_applicationMessageReceivedHandler = new RpcAwareApplicationMessageReceivedHandler(
|
||||
mqttClient.ApplicationMessageReceivedHandler,
|
||||
HandleApplicationMessageReceivedAsync);
|
||||
|
||||
_mqttClient.ApplicationMessageReceivedHandler = _applicationMessageReceivedHandler;
|
||||
}
|
||||
|
||||
public Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
@@ -45,8 +59,31 @@ namespace MQTTnet.Extensions.Rpc
|
||||
throw new ArgumentException("The method name cannot contain /, + or #.");
|
||||
}
|
||||
|
||||
var requestTopic = $"MQTTnet.RPC/{Guid.NewGuid():N}/{methodName}";
|
||||
var responseTopic = requestTopic + "/response";
|
||||
if (!(_mqttClient.ApplicationMessageReceivedHandler is RpcAwareApplicationMessageReceivedHandler))
|
||||
{
|
||||
throw new InvalidOperationException("The application message received handler was modified.");
|
||||
}
|
||||
|
||||
var topicNames = _options.TopicGenerationStrategy.CreateRpcTopics(new TopicGenerationContext
|
||||
{
|
||||
MethodName = methodName,
|
||||
QualityOfServiceLevel = qualityOfServiceLevel,
|
||||
MqttClient = _mqttClient,
|
||||
Options = _options
|
||||
});
|
||||
|
||||
var requestTopic = topicNames.RequestTopic;
|
||||
var responseTopic = topicNames.ResponseTopic;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(requestTopic))
|
||||
{
|
||||
throw new MqttProtocolViolationException("RPC request topic is empty.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(responseTopic))
|
||||
{
|
||||
throw new MqttProtocolViolationException("RPC response topic is empty.");
|
||||
}
|
||||
|
||||
var requestMessage = new MqttApplicationMessageBuilder()
|
||||
.WithTopic(requestTopic)
|
||||
@@ -64,7 +101,7 @@ namespace MQTTnet.Extensions.Rpc
|
||||
|
||||
await _mqttClient.SubscribeAsync(responseTopic, qualityOfServiceLevel).ConfigureAwait(false);
|
||||
await _mqttClient.PublishAsync(requestMessage).ConfigureAwait(false);
|
||||
|
||||
|
||||
using (var timeoutCts = new CancellationTokenSource(timeout))
|
||||
using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token))
|
||||
{
|
||||
@@ -102,26 +139,25 @@ namespace MQTTnet.Extensions.Rpc
|
||||
}
|
||||
}
|
||||
|
||||
private void OnApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
private Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
{
|
||||
if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var tcs))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (tcs.Task.IsCompleted || tcs.Task.IsCanceled)
|
||||
{
|
||||
return;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
tcs.TrySetResult(eventArgs.ApplicationMessage.Payload);
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_mqttClient.ApplicationMessageReceivedHandler = _applicationMessageReceivedHandler.OriginalHandler;
|
||||
|
||||
foreach (var tcs in _waitingCalls)
|
||||
{
|
||||
tcs.Value.SetCanceled();
|
||||
tcs.Value.TrySetCanceled();
|
||||
}
|
||||
|
||||
_waitingCalls.Clear();
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
using MQTTnet.Extensions.Rpc.Options.TopicGeneration;
|
||||
|
||||
namespace MQTTnet.Extensions.Rpc.Options
|
||||
{
|
||||
public interface IMqttRpcClientOptions
|
||||
{
|
||||
IMqttRpcClientTopicGenerationStrategy TopicGenerationStrategy { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using MQTTnet.Extensions.Rpc.Options.TopicGeneration;
|
||||
|
||||
namespace MQTTnet.Extensions.Rpc.Options
|
||||
{
|
||||
public class MqttRpcClientOptions : IMqttRpcClientOptions
|
||||
{
|
||||
public IMqttRpcClientTopicGenerationStrategy TopicGenerationStrategy { get; set; } = new DefaultMqttRpcClientTopicGenerationStrategy();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using MQTTnet.Extensions.Rpc.Options.TopicGeneration;
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Extensions.Rpc.Options
|
||||
{
|
||||
public class MqttRpcClientOptionsBuilder
|
||||
{
|
||||
IMqttRpcClientTopicGenerationStrategy _topicGenerationStrategy = new DefaultMqttRpcClientTopicGenerationStrategy();
|
||||
|
||||
public MqttRpcClientOptionsBuilder WithTopicGenerationStrategy(IMqttRpcClientTopicGenerationStrategy value)
|
||||
{
|
||||
_topicGenerationStrategy = value ?? throw new ArgumentNullException(nameof(value));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public IMqttRpcClientOptions Build()
|
||||
{
|
||||
return new MqttRpcClientOptions
|
||||
{
|
||||
TopicGenerationStrategy = _topicGenerationStrategy
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace MQTTnet.Extensions.Rpc.Options
|
||||
{
|
||||
public class MqttRpcTopicPair
|
||||
{
|
||||
public string RequestTopic { get; set; }
|
||||
|
||||
public string ResponseTopic { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using MQTTnet.Extensions.Rpc.Options.TopicGeneration;
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Extensions.Rpc.Options
|
||||
{
|
||||
public class DefaultMqttRpcClientTopicGenerationStrategy : IMqttRpcClientTopicGenerationStrategy
|
||||
{
|
||||
public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context)
|
||||
{
|
||||
var requestTopic = $"MQTTnet.RPC/{Guid.NewGuid():N}/{context.MethodName}";
|
||||
var responseTopic = requestTopic + "/response";
|
||||
|
||||
return new MqttRpcTopicPair
|
||||
{
|
||||
RequestTopic = requestTopic,
|
||||
ResponseTopic = responseTopic
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace MQTTnet.Extensions.Rpc.Options.TopicGeneration
|
||||
{
|
||||
public interface IMqttRpcClientTopicGenerationStrategy
|
||||
{
|
||||
MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Extensions.Rpc.Options.TopicGeneration
|
||||
{
|
||||
public class TopicGenerationContext
|
||||
{
|
||||
public string MethodName { get; set; }
|
||||
|
||||
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
|
||||
|
||||
public IMqttClient MqttClient { get; set; }
|
||||
|
||||
public IMqttRpcClientOptions Options { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client.Receiving;
|
||||
|
||||
namespace MQTTnet.Extensions.Rpc
|
||||
{
|
||||
public class RpcAwareApplicationMessageReceivedHandler : IMqttApplicationMessageReceivedHandler
|
||||
{
|
||||
private readonly Func<MqttApplicationMessageReceivedEventArgs, Task> _handleReceivedApplicationMessageAsync;
|
||||
|
||||
public RpcAwareApplicationMessageReceivedHandler(
|
||||
IMqttApplicationMessageReceivedHandler originalHandler,
|
||||
Func<MqttApplicationMessageReceivedEventArgs, Task> handleReceivedApplicationMessageAsync)
|
||||
{
|
||||
OriginalHandler = originalHandler;
|
||||
_handleReceivedApplicationMessageAsync = handleReceivedApplicationMessageAsync ?? throw new ArgumentNullException(nameof(handleReceivedApplicationMessageAsync));
|
||||
}
|
||||
|
||||
public IMqttApplicationMessageReceivedHandler OriginalHandler { get; }
|
||||
|
||||
public async Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
{
|
||||
if (OriginalHandler != null)
|
||||
{
|
||||
await OriginalHandler.HandleApplicationMessageReceivedAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await _handleReceivedApplicationMessageAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1</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 />
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'">
|
||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||
<NugetTargetMoniker>UAP,Version=v10.0</NugetTargetMoniker>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion>10.0.18362.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
|
||||
<TargetFrameworkIdentifier>.NETCore</TargetFrameworkIdentifier>
|
||||
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
|
||||
<DefineConstants>$(DefineConstants);WINDOWS_UWP</DefineConstants>
|
||||
<DefaultLanguage>en</DefaultLanguage>
|
||||
<LanguageTargets>$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets</LanguageTargets>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="WebSocket4Net" Version="0.15.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Extensions.WebSocket4Net
|
||||
{
|
||||
public static class MqttFactoryExtensions
|
||||
{
|
||||
public static IMqttFactory UseWebSocket4Net(this IMqttFactory mqttFactory)
|
||||
{
|
||||
if (mqttFactory == null) throw new ArgumentNullException(nameof(mqttFactory));
|
||||
|
||||
return mqttFactory.UseClientAdapterFactory(new WebSocket4NetMqttClientAdapterFactory(mqttFactory.DefaultLogger));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Internal;
|
||||
using SuperSocket.ClientEngine;
|
||||
using WebSocket4Net;
|
||||
|
||||
namespace MQTTnet.Extensions.WebSocket4Net
|
||||
{
|
||||
public class WebSocket4NetMqttChannel : IMqttChannel
|
||||
{
|
||||
private readonly BlockingCollection<byte> _receiveBuffer = new BlockingCollection<byte>();
|
||||
|
||||
private readonly IMqttClientOptions _clientOptions;
|
||||
private readonly MqttClientWebSocketOptions _webSocketOptions;
|
||||
|
||||
private WebSocket _webSocket;
|
||||
|
||||
public WebSocket4NetMqttChannel(IMqttClientOptions clientOptions, MqttClientWebSocketOptions webSocketOptions)
|
||||
{
|
||||
_clientOptions = clientOptions ?? throw new ArgumentNullException(nameof(clientOptions));
|
||||
_webSocketOptions = webSocketOptions ?? throw new ArgumentNullException(nameof(webSocketOptions));
|
||||
}
|
||||
|
||||
public string Endpoint => _webSocketOptions.Uri;
|
||||
|
||||
public bool IsSecureConnection { get; private set; }
|
||||
|
||||
public X509Certificate2 ClientCertificate { get; }
|
||||
|
||||
public async Task ConnectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var uri = _webSocketOptions.Uri;
|
||||
if (!uri.StartsWith("ws://", StringComparison.OrdinalIgnoreCase) && !uri.StartsWith("wss://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (_webSocketOptions.TlsOptions?.UseTls == false)
|
||||
{
|
||||
uri = "ws://" + uri;
|
||||
}
|
||||
else
|
||||
{
|
||||
uri = "wss://" + uri;
|
||||
}
|
||||
}
|
||||
|
||||
var sslProtocols = _webSocketOptions?.TlsOptions?.SslProtocol ?? SslProtocols.None;
|
||||
var subProtocol = _webSocketOptions.SubProtocols.FirstOrDefault() ?? string.Empty;
|
||||
|
||||
var cookies = new List<KeyValuePair<string, string>>();
|
||||
if (_webSocketOptions.CookieContainer != null)
|
||||
{
|
||||
throw new NotSupportedException("Cookies are not supported.");
|
||||
}
|
||||
|
||||
List<KeyValuePair<string, string>> customHeaders = null;
|
||||
if (_webSocketOptions.RequestHeaders != null)
|
||||
{
|
||||
customHeaders = _webSocketOptions.RequestHeaders.Select(i => new KeyValuePair<string, string>(i.Key, i.Value)).ToList();
|
||||
}
|
||||
|
||||
EndPoint proxy = null;
|
||||
if (_webSocketOptions.ProxyOptions != null)
|
||||
{
|
||||
throw new NotSupportedException("Proxies are not supported.");
|
||||
}
|
||||
|
||||
// The user agent can be empty always because it is just added to the custom headers as "User-Agent".
|
||||
var userAgent = string.Empty;
|
||||
|
||||
var origin = string.Empty;
|
||||
var webSocketVersion = WebSocketVersion.None;
|
||||
var receiveBufferSize = 0;
|
||||
|
||||
var certificates = new X509CertificateCollection();
|
||||
if (_webSocketOptions?.TlsOptions?.Certificates != null)
|
||||
{
|
||||
foreach (var certificate in _webSocketOptions.TlsOptions.Certificates)
|
||||
{
|
||||
#if WINDOWS_UWP
|
||||
certificates.Add(new X509Certificate(certificate));
|
||||
#else
|
||||
certificates.Add(certificate);
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
_webSocket = new WebSocket(uri, subProtocol, cookies, customHeaders, userAgent, origin, webSocketVersion, proxy, sslProtocols, receiveBufferSize)
|
||||
{
|
||||
NoDelay = true,
|
||||
Security =
|
||||
{
|
||||
AllowUnstrustedCertificate = _webSocketOptions?.TlsOptions?.AllowUntrustedCertificates == true,
|
||||
AllowCertificateChainErrors = _webSocketOptions?.TlsOptions?.IgnoreCertificateChainErrors == true,
|
||||
Certificates = certificates
|
||||
}
|
||||
};
|
||||
|
||||
await ConnectInternalAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
IsSecureConnection = uri.StartsWith("wss://", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public Task DisconnectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (_webSocket != null && _webSocket.State == WebSocketState.Open)
|
||||
{
|
||||
_webSocket.Close();
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
var readBytes = 0;
|
||||
while (count > 0 && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
if (!_receiveBuffer.TryTake(out var @byte))
|
||||
{
|
||||
if (readBytes == 0)
|
||||
{
|
||||
// Block until at least one byte was received.
|
||||
@byte = _receiveBuffer.Take(cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Task.FromResult(readBytes);
|
||||
}
|
||||
}
|
||||
|
||||
buffer[offset] = @byte;
|
||||
offset++;
|
||||
count--;
|
||||
readBytes++;
|
||||
}
|
||||
|
||||
return Task.FromResult(readBytes);
|
||||
}
|
||||
|
||||
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
_webSocket.Send(buffer, offset, count);
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_webSocket == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_webSocket.DataReceived -= OnDataReceived;
|
||||
_webSocket.Error -= OnError;
|
||||
_webSocket.Dispose();
|
||||
_webSocket = null;
|
||||
}
|
||||
|
||||
private void OnError(object sender, ErrorEventArgs e)
|
||||
{
|
||||
System.Diagnostics.Debug.Write(e.Exception.ToString());
|
||||
}
|
||||
|
||||
private void OnDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
foreach (var @byte in e.Data)
|
||||
{
|
||||
_receiveBuffer.Add(@byte);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ConnectInternalAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_webSocket.Error += OnError;
|
||||
_webSocket.DataReceived += OnDataReceived;
|
||||
|
||||
var taskCompletionSource = new TaskCompletionSource<Exception>();
|
||||
|
||||
void ErrorHandler(object sender, ErrorEventArgs e)
|
||||
{
|
||||
taskCompletionSource.TrySetResult(e.Exception);
|
||||
}
|
||||
|
||||
void SuccessHandler(object sender, EventArgs e)
|
||||
{
|
||||
taskCompletionSource.TrySetResult(null);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_webSocket.Opened += SuccessHandler;
|
||||
_webSocket.Error += ErrorHandler;
|
||||
|
||||
_webSocket.Open();
|
||||
|
||||
var exception = await MqttTaskTimeout.WaitAsync(c =>
|
||||
{
|
||||
c.Register(() => taskCompletionSource.TrySetCanceled());
|
||||
return taskCompletionSource.Task;
|
||||
}, _clientOptions.CommunicationTimeout, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (exception != null)
|
||||
{
|
||||
if (exception is AuthenticationException authenticationException)
|
||||
{
|
||||
throw new MqttCommunicationException(authenticationException.InnerException);
|
||||
}
|
||||
|
||||
if (exception is OperationCanceledException)
|
||||
{
|
||||
throw new MqttCommunicationTimedOutException();
|
||||
}
|
||||
|
||||
throw new MqttCommunicationException(exception);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw new MqttCommunicationTimedOutException();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_webSocket.Opened -= SuccessHandler;
|
||||
_webSocket.Error -= ErrorHandler;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Extensions.WebSocket4Net
|
||||
{
|
||||
public class WebSocket4NetMqttClientAdapterFactory : IMqttClientAdapterFactory
|
||||
{
|
||||
readonly IMqttNetLogger _logger;
|
||||
|
||||
public WebSocket4NetMqttClientAdapterFactory(IMqttNetLogger logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
switch (options.ChannelOptions)
|
||||
{
|
||||
case MqttClientTcpOptions _:
|
||||
{
|
||||
return new MqttChannelAdapter(new MqttTcpChannel(options), new MqttPacketFormatterAdapter(options.ProtocolVersion), _logger);
|
||||
}
|
||||
|
||||
case MqttClientWebSocketOptions webSocketOptions:
|
||||
{
|
||||
return new MqttChannelAdapter(new WebSocket4NetMqttChannel(options, webSocketOptions), new MqttPacketFormatterAdapter(options.ProtocolVersion), _logger);
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace MQTTnet.Server.Configuration
|
||||
{
|
||||
public class RetainedApplicationMessagesModel
|
||||
{
|
||||
public bool Persist { get; set; } = false;
|
||||
|
||||
public int WriteInterval { get; set; } = 10;
|
||||
|
||||
public string Path { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MQTTnet.Server.Configuration
|
||||
{
|
||||
public class ScriptingSettingsModel
|
||||
{
|
||||
public string ScriptsPath { get; set; }
|
||||
|
||||
public List<string> IncludePaths { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
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; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
# 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.
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
//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);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,64 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<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 />
|
||||
<Authors />
|
||||
<PackageId />
|
||||
<SignAssembly>false</SignAssembly>
|
||||
<DelaySign>false</DelaySign>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Version>1.0.0</Version>
|
||||
<TypeScriptToolsVersion>3.3</TypeScriptToolsVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<StartupObject>MQTTnet.Server.Program</StartupObject>
|
||||
<UserSecretsId>c564f0de-28b4-45bf-b726-4d665d705653</UserSecretsId>
|
||||
</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-candidate1" />
|
||||
<PackageReference Include="IronPython.StdLib" Version="2.7.10-candidate1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.3" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.1.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.3.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.3.3" />
|
||||
</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>
|
||||
@@ -0,0 +1,48 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||