mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-18 02:13:00 +08:00
Adjust namespaces.
This commit is contained in:
@@ -20,6 +20,11 @@
|
||||
* [Server] Improved topic filter comparisons (support for $).
|
||||
* [Server] Added more MQTTv5 response information to all interceptors (BREAKING CHANGE!).
|
||||
* [Server] Improved session management for MQTT v5 (#1294, thanks to @logicaloud).
|
||||
* [Server] All interceptors and events are migrated from interfaces to simple events. All existing APIs are availble but must be migrated to corresponding events.
|
||||
* [Server] Removed all interceptor and event interfaces including the delegate implementations etc.
|
||||
* [Server] Renamed a lot of classes and adjsuted namespaces.
|
||||
* [Server] Introduced a new queueing approach for internal message process (packet bus).
|
||||
|
||||
Git commit: $gitCommit
|
||||
</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2021</copyright>
|
||||
|
||||
@@ -1,9 +1,244 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOR/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOREACH/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_IFELSE/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_WHILE/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_METHOD_CALL_PARENTHESES/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_METHOD_PARENTHESES/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_PARENTHESES/@EntryValue">False</s:Boolean>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">180</s:Int64>
|
||||
<s:String x:Key="/Default/CodeStyle/CSharpFileLayoutPatterns/Pattern/@EntryValue"><Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns">
|
||||
<TypePattern DisplayName="Non-reorderable types">
|
||||
<TypePattern.Match>
|
||||
<Or>
|
||||
<And>
|
||||
<Kind Is="Interface" />
|
||||
<Or>
|
||||
<HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" />
|
||||
<HasAttribute Name="System.Runtime.InteropServices.ComImport" />
|
||||
</Or>
|
||||
</And>
|
||||
<Kind Is="Struct" />
|
||||
<HasAttribute Name="JetBrains.Annotations.NoReorderAttribute" />
|
||||
<HasAttribute Name="JetBrains.Annotations.NoReorder" />
|
||||
</Or>
|
||||
</TypePattern.Match>
|
||||
</TypePattern>
|
||||
|
||||
<TypePattern DisplayName="xUnit.net Test Classes" RemoveRegions="All">
|
||||
<TypePattern.Match>
|
||||
<And>
|
||||
<Kind Is="Class" />
|
||||
<HasMember>
|
||||
<And>
|
||||
<Kind Is="Method" />
|
||||
<HasAttribute Name="Xunit.FactAttribute" Inherited="True" />
|
||||
<HasAttribute Name="Xunit.TheoryAttribute" Inherited="True" />
|
||||
</And>
|
||||
</HasMember>
|
||||
</And>
|
||||
</TypePattern.Match>
|
||||
|
||||
<Entry DisplayName="Setup/Teardown Methods">
|
||||
<Entry.Match>
|
||||
<Or>
|
||||
<Kind Is="Constructor" />
|
||||
<And>
|
||||
<Kind Is="Method" />
|
||||
<ImplementsInterface Name="System.IDisposable" />
|
||||
</And>
|
||||
</Or>
|
||||
</Entry.Match>
|
||||
|
||||
<Entry.SortBy>
|
||||
<Kind>
|
||||
<Kind.Order>
|
||||
<DeclarationKind>Constructor</DeclarationKind>
|
||||
</Kind.Order>
|
||||
</Kind>
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
|
||||
|
||||
<Entry DisplayName="All other members" />
|
||||
|
||||
<Entry DisplayName="Test Methods" Priority="100">
|
||||
<Entry.Match>
|
||||
<And>
|
||||
<Kind Is="Method" />
|
||||
<HasAttribute Name="Xunit.FactAttribute" Inherited="false" />
|
||||
<HasAttribute Name="Xunit.TheoryAttribute" Inherited="false" />
|
||||
</And>
|
||||
</Entry.Match>
|
||||
|
||||
<Entry.SortBy>
|
||||
<Name />
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
</TypePattern>
|
||||
|
||||
<TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All">
|
||||
<TypePattern.Match>
|
||||
<And>
|
||||
<Kind Is="Class" />
|
||||
<Or>
|
||||
<HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="true" />
|
||||
<HasAttribute Name="NUnit.Framework.TestFixtureSourceAttribute" Inherited="true" />
|
||||
<HasMember>
|
||||
<And>
|
||||
<Kind Is="Method" />
|
||||
<HasAttribute Name="NUnit.Framework.TestAttribute" Inherited="false" />
|
||||
<HasAttribute Name="NUnit.Framework.TestCaseAttribute" Inherited="false" />
|
||||
<HasAttribute Name="NUnit.Framework.TestCaseSourceAttribute" Inherited="false" />
|
||||
</And>
|
||||
</HasMember>
|
||||
</Or>
|
||||
</And>
|
||||
</TypePattern.Match>
|
||||
|
||||
<Entry DisplayName="Setup/Teardown Methods">
|
||||
<Entry.Match>
|
||||
<And>
|
||||
<Kind Is="Method" />
|
||||
<Or>
|
||||
<HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="true" />
|
||||
<HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="true" />
|
||||
<HasAttribute Name="NUnit.Framework.TestFixtureSetUpAttribute" Inherited="true" />
|
||||
<HasAttribute Name="NUnit.Framework.TestFixtureTearDownAttribute" Inherited="true" />
|
||||
<HasAttribute Name="NUnit.Framework.OneTimeSetUpAttribute" Inherited="true" />
|
||||
<HasAttribute Name="NUnit.Framework.OneTimeTearDownAttribute" Inherited="true" />
|
||||
</Or>
|
||||
</And>
|
||||
</Entry.Match>
|
||||
</Entry>
|
||||
|
||||
<Entry DisplayName="All other members" />
|
||||
|
||||
<Entry DisplayName="Test Methods" Priority="100">
|
||||
<Entry.Match>
|
||||
<And>
|
||||
<Kind Is="Method" />
|
||||
<HasAttribute Name="NUnit.Framework.TestAttribute" Inherited="false" />
|
||||
<HasAttribute Name="NUnit.Framework.TestCaseAttribute" Inherited="false" />
|
||||
<HasAttribute Name="NUnit.Framework.TestCaseSourceAttribute" Inherited="false" />
|
||||
</And>
|
||||
</Entry.Match>
|
||||
|
||||
<Entry.SortBy>
|
||||
<Name />
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
</TypePattern>
|
||||
|
||||
<TypePattern DisplayName="Default Pattern">
|
||||
<Entry DisplayName="Public Delegates" Priority="100">
|
||||
<Entry.Match>
|
||||
<And>
|
||||
<Access Is="Public" />
|
||||
<Kind Is="Delegate" />
|
||||
</And>
|
||||
</Entry.Match>
|
||||
|
||||
<Entry.SortBy>
|
||||
<Name />
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
|
||||
<Entry DisplayName="Public Enums" Priority="100">
|
||||
<Entry.Match>
|
||||
<And>
|
||||
<Access Is="Public" />
|
||||
<Kind Is="Enum" />
|
||||
</And>
|
||||
</Entry.Match>
|
||||
|
||||
<Entry.SortBy>
|
||||
<Name />
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
|
||||
<Entry DisplayName="Static Fields and Constants">
|
||||
<Entry.Match>
|
||||
<Or>
|
||||
<Kind Is="Constant" />
|
||||
<And>
|
||||
<Kind Is="Field" />
|
||||
<Static />
|
||||
</And>
|
||||
</Or>
|
||||
</Entry.Match>
|
||||
|
||||
<Entry.SortBy>
|
||||
<Kind>
|
||||
<Kind.Order>
|
||||
<DeclarationKind>Constant</DeclarationKind>
|
||||
<DeclarationKind>Field</DeclarationKind>
|
||||
</Kind.Order>
|
||||
</Kind>
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
|
||||
<Entry DisplayName="Fields">
|
||||
<Entry.Match>
|
||||
<And>
|
||||
<Kind Is="Field" />
|
||||
<Not>
|
||||
<Static />
|
||||
</Not>
|
||||
</And>
|
||||
</Entry.Match>
|
||||
|
||||
<Entry.SortBy>
|
||||
<Readonly />
|
||||
<Name />
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
|
||||
<Entry DisplayName="Constructors">
|
||||
<Entry.Match>
|
||||
<Kind Is="Constructor" />
|
||||
</Entry.Match>
|
||||
|
||||
<Entry.SortBy>
|
||||
<Static/>
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
|
||||
<Entry DisplayName="Events">
|
||||
<Entry.Match>
|
||||
<Kind Is="Event" />
|
||||
</Entry.Match>
|
||||
|
||||
<Entry.SortBy>
|
||||
<Name Direction="Ascending" />
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
|
||||
<Entry DisplayName="Properties, Indexers">
|
||||
<Entry.Match>
|
||||
<Or>
|
||||
<Kind Is="Property" />
|
||||
<Kind Is="Indexer" />
|
||||
</Or>
|
||||
</Entry.Match>
|
||||
</Entry>
|
||||
|
||||
<Entry DisplayName="All other members">
|
||||
<Entry.SortBy>
|
||||
<Access Order="public internal protected private" />
|
||||
<Name Direction="Ascending" />
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
|
||||
<Entry DisplayName="Nested Types">
|
||||
<Entry.Match>
|
||||
<Kind Is="Type" />
|
||||
</Entry.Match>
|
||||
</Entry>
|
||||
</TypePattern>
|
||||
</Patterns>
|
||||
</s:String>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||
<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>
|
||||
|
||||
@@ -10,6 +10,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Packets;
|
||||
using IMqttClient = MQTTnet.Client.IMqttClient;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
|
||||
@@ -6,14 +6,14 @@ namespace MQTTnet.Formatter
|
||||
{
|
||||
public sealed class MqttConnAckPacketFactory
|
||||
{
|
||||
public MqttConnAckPacket Create(ValidatingConnectionEventArgs clientConnectionValidationEventArgs)
|
||||
public MqttConnAckPacket Create(ValidatingConnectionEventArgs validatingConnectionEventArgs)
|
||||
{
|
||||
if (clientConnectionValidationEventArgs == null) throw new ArgumentNullException(nameof(clientConnectionValidationEventArgs));
|
||||
if (validatingConnectionEventArgs == null) throw new ArgumentNullException(nameof(validatingConnectionEventArgs));
|
||||
|
||||
var connAckPacket = new MqttConnAckPacket
|
||||
{
|
||||
ReturnCode = MqttConnectReasonCodeConverter.ToConnectReturnCode(clientConnectionValidationEventArgs.ReasonCode),
|
||||
ReasonCode = clientConnectionValidationEventArgs.ReasonCode,
|
||||
ReturnCode = MqttConnectReasonCodeConverter.ToConnectReturnCode(validatingConnectionEventArgs.ReasonCode),
|
||||
ReasonCode = validatingConnectionEventArgs.ReasonCode,
|
||||
Properties =
|
||||
{
|
||||
RetainAvailable = true,
|
||||
@@ -22,17 +22,17 @@ namespace MQTTnet.Formatter
|
||||
TopicAliasMaximum = ushort.MaxValue,
|
||||
WildcardSubscriptionAvailable = true,
|
||||
|
||||
AuthenticationMethod = clientConnectionValidationEventArgs.AuthenticationMethod,
|
||||
AuthenticationData = clientConnectionValidationEventArgs.ResponseAuthenticationData,
|
||||
AssignedClientIdentifier = clientConnectionValidationEventArgs.AssignedClientIdentifier,
|
||||
ReasonString = clientConnectionValidationEventArgs.ReasonString,
|
||||
ServerReference = clientConnectionValidationEventArgs.ServerReference
|
||||
AuthenticationMethod = validatingConnectionEventArgs.AuthenticationMethod,
|
||||
AuthenticationData = validatingConnectionEventArgs.ResponseAuthenticationData,
|
||||
AssignedClientIdentifier = validatingConnectionEventArgs.AssignedClientIdentifier,
|
||||
ReasonString = validatingConnectionEventArgs.ReasonString,
|
||||
ServerReference = validatingConnectionEventArgs.ServerReference
|
||||
}
|
||||
};
|
||||
|
||||
if (clientConnectionValidationEventArgs.ResponseUserProperties != null)
|
||||
if (validatingConnectionEventArgs.ResponseUserProperties != null)
|
||||
{
|
||||
connAckPacket.Properties.UserProperties.AddRange(clientConnectionValidationEventArgs.ResponseUserProperties);
|
||||
connAckPacket.Properties.UserProperties.AddRange(validatingConnectionEventArgs.ResponseUserProperties);
|
||||
}
|
||||
|
||||
return connAckPacket;
|
||||
|
||||
@@ -6,6 +6,14 @@ namespace MQTTnet.Formatter
|
||||
{
|
||||
public sealed class MqttDisconnectPacketFactory
|
||||
{
|
||||
public MqttDisconnectPacket Create(MqttDisconnectReasonCode reasonCode)
|
||||
{
|
||||
return new MqttDisconnectPacket
|
||||
{
|
||||
ReasonCode = reasonCode
|
||||
};
|
||||
}
|
||||
|
||||
public MqttDisconnectPacket Create(MqttClientDisconnectOptions clientDisconnectOptions)
|
||||
{
|
||||
var packet = new MqttDisconnectPacket();
|
||||
|
||||
@@ -2,39 +2,49 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Diagnostics;
|
||||
|
||||
namespace MQTTnet.Internal
|
||||
{
|
||||
public sealed class AsyncEvent<TEventArgs> where TEventArgs : EventArgs
|
||||
{
|
||||
readonly List<Func<TEventArgs, Task>> _handlers = new List<Func<TEventArgs, Task>>();
|
||||
readonly List<AsyncEventInvocator<TEventArgs>> _handlers = new List<AsyncEventInvocator<TEventArgs>>();
|
||||
|
||||
public void AddHandler(Func<TEventArgs, Task> handler)
|
||||
{
|
||||
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
||||
|
||||
_handlers.Add(handler);
|
||||
}
|
||||
if (handler == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
public void RemoveHandler(Func<TEventArgs, Task> handler)
|
||||
_handlers.Add(new AsyncEventInvocator<TEventArgs>(null, handler));
|
||||
}
|
||||
|
||||
public void AddHandler(Action<TEventArgs> handler)
|
||||
{
|
||||
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
||||
|
||||
_handlers.Remove(handler);
|
||||
if (handler == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
_handlers.Add(new AsyncEventInvocator<TEventArgs>(handler, null));
|
||||
}
|
||||
|
||||
public async Task InvokeAsync(TEventArgs eventArgs)
|
||||
{
|
||||
foreach (var handler in _handlers)
|
||||
{
|
||||
await handler.Invoke(eventArgs);
|
||||
await handler.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<TEventArgs> InvokeAsync(Func<TEventArgs> eventArgsProvider)
|
||||
{
|
||||
if (eventArgsProvider == null) throw new ArgumentNullException(nameof(eventArgsProvider));
|
||||
|
||||
if (eventArgsProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(eventArgsProvider));
|
||||
}
|
||||
|
||||
if (!_handlers.Any())
|
||||
{
|
||||
return default;
|
||||
@@ -44,5 +54,47 @@ namespace MQTTnet.Internal
|
||||
await InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
return eventArgs;
|
||||
}
|
||||
|
||||
public void RemoveHandler(Func<TEventArgs, Task> handler)
|
||||
{
|
||||
if (handler == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
_handlers.RemoveAll(h => h.WrapsHandler(handler));
|
||||
}
|
||||
|
||||
public void RemoveHandler(Action<TEventArgs> handler)
|
||||
{
|
||||
if (handler == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
_handlers.RemoveAll(h => h.WrapsHandler(handler));
|
||||
}
|
||||
|
||||
public async Task TryInvokeAsync(TEventArgs eventArgs, MqttNetSourceLogger logger)
|
||||
{
|
||||
if (eventArgs == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(eventArgs));
|
||||
}
|
||||
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
logger.Warning(exception, $"Error while invoking event ({typeof(TEventArgs)}).");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Implementations;
|
||||
|
||||
namespace MQTTnet.Internal
|
||||
{
|
||||
public sealed class AsyncEventInvocator<TEventArgs>
|
||||
{
|
||||
readonly Action<TEventArgs> _handler;
|
||||
readonly Func<TEventArgs, Task> _asyncHandler;
|
||||
|
||||
public AsyncEventInvocator(Action<TEventArgs> handler, Func<TEventArgs, Task> asyncHandler)
|
||||
{
|
||||
_handler = handler;
|
||||
_asyncHandler = asyncHandler;
|
||||
}
|
||||
|
||||
public bool WrapsHandler(Action<TEventArgs> handler)
|
||||
{
|
||||
return ReferenceEquals(_handler, handler);
|
||||
}
|
||||
|
||||
public bool WrapsHandler(Func<TEventArgs, Task> handler)
|
||||
{
|
||||
return ReferenceEquals(_asyncHandler, handler);
|
||||
}
|
||||
|
||||
public Task InvokeAsync(TEventArgs eventArgs)
|
||||
{
|
||||
if (_handler != null)
|
||||
{
|
||||
_handler.Invoke(eventArgs);
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
}
|
||||
|
||||
if (_asyncHandler != null)
|
||||
{
|
||||
return _asyncHandler.Invoke(eventArgs);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,7 @@ namespace MQTTnet.Internal
|
||||
{
|
||||
public sealed class MqttPacketBus : IDisposable
|
||||
{
|
||||
readonly object _syncRoot = new object();
|
||||
|
||||
readonly LinkedList<MqttPacketBusItem>[] _partitions = new LinkedList<MqttPacketBusItem>[]
|
||||
readonly LinkedList<MqttPacketBusItem>[] _partitions =
|
||||
{
|
||||
new LinkedList<MqttPacketBusItem>(),
|
||||
new LinkedList<MqttPacketBusItem>(),
|
||||
@@ -19,10 +17,11 @@ namespace MQTTnet.Internal
|
||||
};
|
||||
|
||||
readonly SemaphoreSlim _semaphore = new SemaphoreSlim(0);
|
||||
readonly object _syncRoot = new object();
|
||||
|
||||
int _activePartition = (int) MqttPacketBusPartition.Health;
|
||||
|
||||
public int PacketsCount
|
||||
public int ItemsCount
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -33,50 +32,18 @@ namespace MQTTnet.Internal
|
||||
}
|
||||
}
|
||||
|
||||
public int DataPacketsCount
|
||||
public void Clear()
|
||||
{
|
||||
get
|
||||
lock (_syncRoot)
|
||||
{
|
||||
lock (_syncRoot)
|
||||
foreach (var partition in _partitions)
|
||||
{
|
||||
return _partitions[(int) MqttPacketBusPartition.Data].Count;
|
||||
partition.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<MqttBasePacket> ExportPackets(MqttPacketBusPartition partition)
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
return _partitions[(int) partition].Select(i => i.Packet).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public void Enqueue(MqttPacketBusItem item, MqttPacketBusPartition partition)
|
||||
{
|
||||
if (item == null) throw new ArgumentNullException(nameof(item));
|
||||
|
||||
lock (_syncRoot)
|
||||
{
|
||||
_partitions[(int) partition].AddLast(item);
|
||||
}
|
||||
|
||||
_semaphore.Release();
|
||||
}
|
||||
|
||||
void MoveActivePartition()
|
||||
{
|
||||
if (_activePartition >= _partitions.Length - 1)
|
||||
{
|
||||
_activePartition = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_activePartition++;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<MqttPacketBusItem> DequeueAsync(CancellationToken cancellationToken)
|
||||
public async Task<MqttPacketBusItem> DequeueItemAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -99,7 +66,7 @@ namespace MQTTnet.Internal
|
||||
// the worker back to the thread pool.
|
||||
await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
throw new InvalidOperationException("MqttPacketBus is broken.");
|
||||
@@ -110,15 +77,60 @@ namespace MQTTnet.Internal
|
||||
_semaphore?.Dispose();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
public void DropFirstItem(MqttPacketBusPartition partition)
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
foreach (var partition in _partitions)
|
||||
var partitionInstance = _partitions[(int) partition];
|
||||
|
||||
if (partitionInstance.Any())
|
||||
{
|
||||
partition.Clear();
|
||||
partitionInstance.RemoveFirst();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void EnqueueItem(MqttPacketBusItem item, MqttPacketBusPartition partition)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(item));
|
||||
}
|
||||
|
||||
lock (_syncRoot)
|
||||
{
|
||||
_partitions[(int) partition].AddLast(item);
|
||||
}
|
||||
|
||||
_semaphore.Release();
|
||||
}
|
||||
|
||||
public List<MqttBasePacket> ExportPackets(MqttPacketBusPartition partition)
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
return _partitions[(int) partition].Select(i => i.Packet).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
void MoveActivePartition()
|
||||
{
|
||||
if (_activePartition >= _partitions.Length - 1)
|
||||
{
|
||||
_activePartition = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_activePartition++;
|
||||
}
|
||||
}
|
||||
|
||||
public int PartitionItemsCount(MqttPacketBusPartition partition)
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
return _partitions[(int) partition].Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=diagnostics_005Cruntime/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=server_005Cevents/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=server_005Cinternal/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=server_005Cmanagement/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=server_005Coptions/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=server_005Cstatus/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=server_005Cstorage/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
@@ -7,6 +7,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MQTTnet.Diagnostics;
|
||||
using IMqttClient = MQTTnet.Client.IMqttClient;
|
||||
using MqttClient = MQTTnet.Client.MqttClient;
|
||||
|
||||
namespace MQTTnet
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
@@ -17,7 +18,7 @@ namespace MQTTnet.Server
|
||||
/// <summary>
|
||||
/// Gets or sets a key/value collection that can be used to share data within the scope of this session.
|
||||
/// </summary>
|
||||
public IDictionary<object, object> SessionItems { get; internal set; }
|
||||
public IDictionary SessionItems { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the response which will be sent to the client via the PUBACK etc. packets.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using System.Threading;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace MQTTnet.Server
|
||||
/// <summary>
|
||||
/// Gets or sets a key/value collection that can be used to share data within the scope of this session.
|
||||
/// </summary>
|
||||
public IDictionary<object, object> SessionItems { get; internal set; }
|
||||
public IDictionary SessionItems { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the broker should create an internal subscription for the client.
|
||||
@@ -39,7 +39,7 @@ namespace MQTTnet.Server
|
||||
/// <summary>
|
||||
/// Gets the current client session.
|
||||
/// </summary>
|
||||
public IMqttSessionStatus Session { get; internal set; }
|
||||
public MqttSessionStatus Session { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the response which will be sent to the client via the SUBACK packet.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
@@ -22,7 +23,7 @@ namespace MQTTnet.Server
|
||||
/// <summary>
|
||||
/// Gets or sets a key/value collection that can be used to share data within the scope of this session.
|
||||
/// </summary>
|
||||
public IDictionary<object, object> SessionItems { get; internal set; }
|
||||
public IDictionary SessionItems { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the response which will be sent to the client via the UNSUBACK pocket.
|
||||
|
||||
@@ -5,6 +5,6 @@ namespace MQTTnet.Server
|
||||
{
|
||||
public sealed class LoadingRetainedMessagesEventArgs : EventArgs
|
||||
{
|
||||
public List<MqttApplicationMessage> StoredRetainedMessages { get; set; } = new List<MqttApplicationMessage>();
|
||||
public List<MqttApplicationMessage> LoadedRetainedMessages { get; set; } = new List<MqttApplicationMessage>();
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ namespace MQTTnet.Server
|
||||
|
||||
public List<MqttPublishPacket> PublishPackets { get; } = new List<MqttPublishPacket>();
|
||||
|
||||
IDictionary<object, object> Items { get; set; }
|
||||
public IDictionary<object, object> Items { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last will message.
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public sealed class SessionDeletedEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the ID of the session.
|
||||
/// </summary>
|
||||
public string Id { get; internal set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
@@ -122,7 +123,7 @@ namespace MQTTnet.Server
|
||||
/// <summary>
|
||||
/// Gets or sets a key/value collection that can be used to share data within the scope of this session.
|
||||
/// </summary>
|
||||
public IDictionary<object, object> SessionItems { get; internal set; }
|
||||
public IDictionary SessionItems { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is used for MQTTv3 only.
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
@@ -9,9 +10,9 @@ namespace MQTTnet.Server
|
||||
{
|
||||
readonly MqttServerEventContainer _eventContainer;
|
||||
readonly string _clientId;
|
||||
readonly IDictionary<object, object> _sessionItems;
|
||||
readonly IDictionary _sessionItems;
|
||||
|
||||
public MqttApplicationMessageInterceptorInvoker(MqttServerEventContainer eventContainer, string clientId, IDictionary<object, object> sessionItems)
|
||||
public MqttApplicationMessageInterceptorInvoker(MqttServerEventContainer eventContainer, string clientId, IDictionary sessionItems)
|
||||
{
|
||||
_eventContainer = eventContainer ?? throw new ArgumentNullException(nameof(eventContainer));
|
||||
_clientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
|
||||
@@ -49,7 +50,7 @@ namespace MQTTnet.Server
|
||||
{
|
||||
// This can happen if a topic alias us used but the topic is
|
||||
// unknown to the server.
|
||||
eventArgs.Response.ReasonCode = MqttApplicationMessageResponseReasonCode.TopicNameInvalid;
|
||||
eventArgs.Response.ReasonCode = MqttPubAckReasonCode.TopicNameInvalid;
|
||||
eventArgs.ProcessPublish = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,11 +75,11 @@ namespace MQTTnet.Server
|
||||
|
||||
public bool IsCleanDisconnect { get; private set; }
|
||||
|
||||
public async Task StopAsync(MqttClientDisconnectReason reason)
|
||||
public async Task StopAsync(MqttDisconnectReasonCode reason)
|
||||
{
|
||||
IsRunning = false;
|
||||
|
||||
if (reason == MqttClientDisconnectReason.SessionTakenOver || reason == MqttClientDisconnectReason.KeepAliveTimeout)
|
||||
if (reason == MqttDisconnectReasonCode.SessionTakenOver || reason == MqttDisconnectReasonCode.KeepAliveTimeout)
|
||||
{
|
||||
// Is is very important to send the DISCONNECT packet here BEFORE cancelling the
|
||||
// token because the entire connection is closed (disposed) as soon as the cancellation
|
||||
@@ -391,7 +391,7 @@ namespace MQTTnet.Server
|
||||
|
||||
if (_applicationMessageInterceptorInvoker.CloseConnection)
|
||||
{
|
||||
await StopAsync(MqttClientDisconnectReason.UnspecifiedError);
|
||||
await StopAsync(MqttDisconnectReasonCode.UnspecifiedError);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -455,18 +455,12 @@ namespace MQTTnet.Server
|
||||
}
|
||||
}
|
||||
|
||||
async Task TrySendDisconnectPacket(MqttClientDisconnectReason reason)
|
||||
async Task TrySendDisconnectPacket(MqttDisconnectReasonCode reasonCode)
|
||||
{
|
||||
try
|
||||
{
|
||||
var disconnectOptions = new MqttClientDisconnectOptions
|
||||
{
|
||||
Reason = reason,
|
||||
ReasonString = reason.ToString()
|
||||
};
|
||||
|
||||
var disconnectPacket = _packetFactories.Disconnect.Create(disconnectOptions);
|
||||
|
||||
var disconnectPacket = _packetFactories.Disconnect.Create(reasonCode);
|
||||
|
||||
using (var timeout = new CancellationTokenSource(_serverOptions.DefaultCommunicationTimeout))
|
||||
{
|
||||
await SendPacketAsync(disconnectPacket, timeout.Token).ConfigureAwait(false);
|
||||
@@ -474,7 +468,7 @@ namespace MQTTnet.Server
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Warning(exception, "Client '{0}': Error while sending DISCONNECT packet (Reason = {1}).", Id, reason);
|
||||
_logger.Warning(exception, "Client '{0}': Error while sending DISCONNECT packet (ReasonCode = {1}).", Id, reasonCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -22,11 +23,7 @@ namespace MQTTnet.Server
|
||||
readonly AsyncLock _createConnectionSyncRoot = new AsyncLock();
|
||||
readonly Dictionary<string, MqttClient> _clients = new Dictionary<string, MqttClient>(4096);
|
||||
readonly Dictionary<string, MqttSession> _sessions = new Dictionary<string, MqttSession>(4096);
|
||||
|
||||
readonly IDictionary<object, object> _serverSessionItems = new ConcurrentDictionary<object, object>();
|
||||
|
||||
readonly MqttConnAckPacketFactory _connAckPacketFactory = new MqttConnAckPacketFactory();
|
||||
|
||||
|
||||
readonly MqttRetainedMessagesManager _retainedMessagesManager;
|
||||
readonly MqttServerEventContainer _eventContainer;
|
||||
readonly MqttServerOptions _options;
|
||||
@@ -89,21 +86,18 @@ namespace MQTTnet.Server
|
||||
return;
|
||||
}
|
||||
|
||||
MqttConnAckPacket connAckPacket;
|
||||
var validatingConnectionEventArgs = await ValidateConnection(connectPacket, channelAdapter).ConfigureAwait(false);
|
||||
var connAckPacket = _packetFactories.ConnAck.Create(validatingConnectionEventArgs);
|
||||
|
||||
var connectionValidatorContext = await ValidateConnection(connectPacket, channelAdapter).ConfigureAwait(false);
|
||||
if (connectionValidatorContext.ReasonCode != MqttConnectReasonCode.Success)
|
||||
if (validatingConnectionEventArgs.ReasonCode != MqttConnectReasonCode.Success)
|
||||
{
|
||||
// Send failure response here without preparing a connection and session!
|
||||
connAckPacket = _connAckPacketFactory.Create(connectionValidatorContext);
|
||||
await channelAdapter.SendPacketAsync(connAckPacket, cancellationToken).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
connAckPacket = _connAckPacketFactory.Create(connectionValidatorContext);
|
||||
|
||||
// Pass connAckPacket so that IsSessionPresent flag can be set if the client session already exists
|
||||
client = await CreateClientConnection(connectPacket, connAckPacket, channelAdapter, connectionValidatorContext).ConfigureAwait(false);
|
||||
// Pass connAckPacket so that IsSessionPresent flag can be set if the client session already exists.
|
||||
client = await CreateClientConnection(connectPacket, connAckPacket, channelAdapter, validatingConnectionEventArgs).ConfigureAwait(false);
|
||||
|
||||
await client.SendPacketAsync(connAckPacket, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
@@ -176,21 +170,34 @@ namespace MQTTnet.Server
|
||||
|
||||
foreach (var connection in connections)
|
||||
{
|
||||
await connection.StopAsync(MqttClientDisconnectReason.NormalDisconnection).ConfigureAwait(false);
|
||||
await connection.StopAsync(MqttDisconnectReasonCode.NormalDisconnection).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public List<MqttClient> GetConnections()
|
||||
public List<MqttClient> GetClients()
|
||||
{
|
||||
lock (_clients)
|
||||
{
|
||||
return _clients.Values.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public Task<IList<IMqttClientStatus>> GetClientStatusAsync()
|
||||
|
||||
public MqttClient GetClient(string id)
|
||||
{
|
||||
var result = new List<IMqttClientStatus>();
|
||||
lock (_clients)
|
||||
{
|
||||
if (!_clients.TryGetValue(id, out var client))
|
||||
{
|
||||
throw new InvalidOperationException($"Client with ID '{id}' not found.");
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
}
|
||||
|
||||
public Task<IList<MqttClientStatus>> GetClientStatusAsync()
|
||||
{
|
||||
var result = new List<MqttClientStatus>();
|
||||
|
||||
lock (_clients)
|
||||
{
|
||||
@@ -205,12 +212,12 @@ namespace MQTTnet.Server
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult((IList<IMqttClientStatus>)result);
|
||||
return Task.FromResult((IList<MqttClientStatus>)result);
|
||||
}
|
||||
|
||||
public Task<IList<IMqttSessionStatus>> GetSessionStatusAsync()
|
||||
public Task<IList<MqttSessionStatus>> GetSessionStatusAsync()
|
||||
{
|
||||
var result = new List<IMqttSessionStatus>();
|
||||
var result = new List<MqttSessionStatus>();
|
||||
|
||||
lock (_sessions)
|
||||
{
|
||||
@@ -221,15 +228,8 @@ namespace MQTTnet.Server
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult((IList<IMqttSessionStatus>)result);
|
||||
return Task.FromResult((IList<MqttSessionStatus>)result);
|
||||
}
|
||||
|
||||
// public void DispatchPublishPacket(MqttPublishPacket publishPacket, MqttClientConnection sender)
|
||||
// {
|
||||
// if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
|
||||
//
|
||||
// _messageQueue.Add(new MqttPendingApplicationMessage(publishPacket, sender));
|
||||
// }
|
||||
|
||||
public async Task SubscribeAsync(string clientId, ICollection<MqttTopicFilter> topicFilters)
|
||||
{
|
||||
@@ -281,7 +281,7 @@ namespace MQTTnet.Server
|
||||
{
|
||||
if (connection != null)
|
||||
{
|
||||
await connection.StopAsync(MqttClientDisconnectReason.NormalDisconnection).ConfigureAwait(false);
|
||||
await connection.StopAsync(MqttDisconnectReasonCode.NormalDisconnection).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
@@ -291,7 +291,10 @@ namespace MQTTnet.Server
|
||||
|
||||
try
|
||||
{
|
||||
session?.OnDeleted();
|
||||
await _eventContainer.SessionDeletedEvent.TryInvokeAsync(new SessionDeletedEventArgs
|
||||
{
|
||||
Id = session.Id
|
||||
}, _logger).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -302,31 +305,7 @@ namespace MQTTnet.Server
|
||||
|
||||
_logger.Verbose("Session for client '{0}' deleted.", clientId);
|
||||
}
|
||||
|
||||
// public void Dispose()
|
||||
// {
|
||||
// _messageQueue?.Dispose();
|
||||
// }
|
||||
|
||||
// async Task TryProcessQueuedApplicationMessagesAsync(CancellationToken cancellationToken)
|
||||
// {
|
||||
// // Make sure all queued messages are processed before server stops.
|
||||
// while (!cancellationToken.IsCancellationRequested || _messageQueue.Any())
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// await TryProcessNextQueuedApplicationMessage().ConfigureAwait(false);
|
||||
// }
|
||||
// catch (OperationCanceledException)
|
||||
// {
|
||||
// }
|
||||
// catch (Exception exception)
|
||||
// {
|
||||
// _logger.Error(exception, "Unhandled exception while processing queued application messages.");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
public async Task DispatchPublishPacket(string senderClientId, MqttApplicationMessage applicationMessage)
|
||||
{
|
||||
try
|
||||
@@ -501,7 +480,7 @@ namespace MQTTnet.Server
|
||||
if (existing != null)
|
||||
{
|
||||
existing.IsTakenOver = true;
|
||||
await existing.StopAsync(MqttClientDisconnectReason.SessionTakenOver).ConfigureAwait(false);
|
||||
await existing.StopAsync(MqttDisconnectReasonCode.SessionTakenOver).ConfigureAwait(false);
|
||||
|
||||
await _eventContainer.ClientDisconnectedEvent.InvokeAsync(() => new ClientDisconnectedEventArgs
|
||||
{
|
||||
@@ -514,40 +493,7 @@ namespace MQTTnet.Server
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
// async Task<InterceptingMqttClientPublishEventArgs> InterceptApplicationMessageAsync(
|
||||
// IMqttServerApplicationMessageInterceptor interceptor,
|
||||
// MqttClientConnection clientConnection,
|
||||
// MqttApplicationMessage applicationMessage)
|
||||
// {
|
||||
// string senderClientId;
|
||||
// IDictionary<object, object> sessionItems;
|
||||
//
|
||||
// var messageIsFromServer = clientConnection == null;
|
||||
// if (messageIsFromServer)
|
||||
// {
|
||||
// senderClientId = _options.ClientId;
|
||||
// sessionItems = _serverSessionItems;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// senderClientId = clientConnection.Id;
|
||||
// sessionItems = clientConnection.Session.Items;
|
||||
// }
|
||||
//
|
||||
// var interceptorContext = new InterceptingMqttClientPublishEventArgs
|
||||
// {
|
||||
// ClientId = senderClientId,
|
||||
// SessionItems = sessionItems,
|
||||
// ProcessPublish = true,
|
||||
// ApplicationMessage = applicationMessage,
|
||||
// CloseConnection = false
|
||||
// };
|
||||
//
|
||||
// await interceptor.InterceptApplicationMessagePublishAsync(interceptorContext).ConfigureAwait(false);
|
||||
// return interceptorContext;
|
||||
// }
|
||||
|
||||
|
||||
MqttSession GetClientSession(string clientId)
|
||||
{
|
||||
lock (_sessions)
|
||||
@@ -573,7 +519,7 @@ namespace MQTTnet.Server
|
||||
_rootLogger);
|
||||
}
|
||||
|
||||
MqttSession CreateSession(string clientId, IDictionary<object, object> sessionItems, bool isPersistent)
|
||||
MqttSession CreateSession(string clientId, IDictionary sessionItems, bool isPersistent)
|
||||
{
|
||||
_logger.Verbose("Created a new session for client '{0}'.", clientId);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -13,22 +14,22 @@ namespace MQTTnet.Server
|
||||
// We use a reader writer lock because the subscriptions are only read most of the time.
|
||||
// Since writing is done by multiple threads (server API or connection thread), we cannot avoid locking
|
||||
// completely by swapping references etc.
|
||||
readonly ReaderWriterLockSlim _subscriptionsLock = new ReaderWriterLockSlim();
|
||||
readonly Dictionary<string, MqttSubscription> _subscriptions = new Dictionary<string, MqttSubscription>(4096);
|
||||
readonly SemaphoreSlim _subscriptionsLock = new SemaphoreSlim(1);
|
||||
|
||||
// The subscriptions are stored as a ConcurrentDictionary in order ensure that reading the data is save.
|
||||
// The additional lock is important to coordinate complex update logic with multiple steps, checks and interceptors.
|
||||
readonly ConcurrentDictionary<string, MqttSubscription> _subscriptions = new ConcurrentDictionary<string, MqttSubscription>();
|
||||
|
||||
readonly MqttSession _session;
|
||||
readonly MqttServerOptions _options;
|
||||
readonly MqttServerEventContainer _eventContainer;
|
||||
readonly MqttRetainedMessagesManager _retainedMessagesManager;
|
||||
|
||||
public MqttClientSubscriptionsManager(
|
||||
MqttSession session,
|
||||
MqttServerOptions serverOptions,
|
||||
MqttServerEventContainer eventContainer,
|
||||
MqttRetainedMessagesManager retainedMessagesManager)
|
||||
{
|
||||
_session = session ?? throw new ArgumentNullException(nameof(session));
|
||||
_options = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions));
|
||||
_eventContainer = eventContainer ?? throw new ArgumentNullException(nameof(eventContainer));
|
||||
_retainedMessagesManager = retainedMessagesManager ?? throw new ArgumentNullException(nameof(retainedMessagesManager));
|
||||
}
|
||||
@@ -98,7 +99,7 @@ namespace MQTTnet.Server
|
||||
|
||||
var result = new MqttUnsubscribeResult();
|
||||
|
||||
_subscriptionsLock.EnterWriteLock();
|
||||
await _subscriptionsLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
foreach (var topicFilter in unsubscribePacket.TopicFilters)
|
||||
@@ -124,13 +125,13 @@ namespace MQTTnet.Server
|
||||
|
||||
if (interceptorContext.ProcessUnsubscription)
|
||||
{
|
||||
_subscriptions.Remove(topicFilter);
|
||||
_subscriptions.TryRemove(topicFilter, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_subscriptionsLock.ExitWriteLock();
|
||||
_subscriptionsLock.Release();
|
||||
}
|
||||
|
||||
foreach (var topicFilter in unsubscribePacket.TopicFilters)
|
||||
@@ -148,17 +149,7 @@ namespace MQTTnet.Server
|
||||
public CheckSubscriptionsResult CheckSubscriptions(string topic, MqttQualityOfServiceLevel qosLevel,
|
||||
string senderClientId)
|
||||
{
|
||||
List<MqttSubscription> subscriptions;
|
||||
_subscriptionsLock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
subscriptions = _subscriptions.Values.ToList();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_subscriptionsLock.ExitReadLock();
|
||||
}
|
||||
|
||||
var subscriptions = _subscriptions.Values.ToList();
|
||||
var senderIsReceiver = string.Equals(senderClientId, _session.Id);
|
||||
|
||||
var qosLevels = new HashSet<MqttQualityOfServiceLevel>();
|
||||
@@ -243,7 +234,7 @@ namespace MQTTnet.Server
|
||||
|
||||
bool isNewSubscription;
|
||||
|
||||
_subscriptionsLock.EnterWriteLock();
|
||||
_subscriptionsLock.Wait();
|
||||
try
|
||||
{
|
||||
isNewSubscription = !_subscriptions.ContainsKey(topicFilter.Topic);
|
||||
@@ -251,7 +242,7 @@ namespace MQTTnet.Server
|
||||
}
|
||||
finally
|
||||
{
|
||||
_subscriptionsLock.ExitWriteLock();
|
||||
_subscriptionsLock.Release();
|
||||
}
|
||||
|
||||
return new CreateSubscriptionResult
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace MQTTnet.Server
|
||||
{
|
||||
_messages.Clear();
|
||||
|
||||
foreach (var retainedMessage in eventArgs.StoredRetainedMessages ?? new List<MqttApplicationMessage>())
|
||||
foreach (var retainedMessage in eventArgs.LoadedRetainedMessages ?? new List<MqttApplicationMessage>())
|
||||
{
|
||||
_messages[retainedMessage.Topic] = retainedMessage;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace MQTTnet.Server
|
||||
|
||||
public AsyncEvent<PreparingSessionEventArgs> PreparingSessionEvent { get; } = new AsyncEvent<PreparingSessionEventArgs>();
|
||||
|
||||
public AsyncEvent<SessionDeletedEventArgs> SessionDeletedEvent { get; } = new AsyncEvent<SessionDeletedEventArgs>();
|
||||
|
||||
public AsyncEvent<ApplicationMessageNotConsumedEventArgs> ApplicationMessageNotConsumedEvent { get; } = new AsyncEvent<ApplicationMessageNotConsumedEventArgs>();
|
||||
|
||||
public AsyncEvent<RetainedMessageChangedEventArgs> RetainedMessageChangedEvent { get; } = new AsyncEvent<RetainedMessageChangedEventArgs>();
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
@@ -40,7 +40,7 @@ namespace MQTTnet.Server
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
TryMaintainConnections();
|
||||
TryProcessClients();
|
||||
PlatformAbstractionLayer.Sleep(_options.KeepAliveMonitorInterval);
|
||||
}
|
||||
}
|
||||
@@ -57,16 +57,16 @@ namespace MQTTnet.Server
|
||||
}
|
||||
}
|
||||
|
||||
void TryMaintainConnections()
|
||||
void TryProcessClients()
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
foreach (var connection in _sessionsManager.GetConnections())
|
||||
foreach (var client in _sessionsManager.GetClients())
|
||||
{
|
||||
TryMaintainConnection(connection, now);
|
||||
TryProcessClient(client, now);
|
||||
}
|
||||
}
|
||||
|
||||
void TryMaintainConnection(MqttClient connection, DateTime now)
|
||||
void TryProcessClient(MqttClient connection, DateTime now)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -108,7 +108,7 @@ namespace MQTTnet.Server
|
||||
// We do not need to wait for the task so no await is needed.
|
||||
// Also the internal state of the connection must be swapped to "Finalizing" because the
|
||||
// next iteration of the keep alive timer happens.
|
||||
var _ = connection.StopAsync(MqttClientDisconnectReason.KeepAliveTimeout);
|
||||
var _ = connection.StopAsync(MqttDisconnectReasonCode.KeepAliveTimeout);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -21,7 +22,7 @@ namespace MQTTnet.Server
|
||||
|
||||
public MqttSession(string clientId,
|
||||
bool isPersistent,
|
||||
IDictionary<object, object> items,
|
||||
IDictionary items,
|
||||
MqttServerOptions serverOptions,
|
||||
MqttServerEventContainer eventContainer,
|
||||
MqttRetainedMessagesManager retainedMessagesManager,
|
||||
@@ -34,11 +35,9 @@ namespace MQTTnet.Server
|
||||
_serverOptions = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions));
|
||||
_clientSessionsManager = clientSessionsManager ?? throw new ArgumentNullException(nameof(clientSessionsManager));
|
||||
|
||||
SubscriptionsManager = new MqttClientSubscriptionsManager(this, serverOptions, eventContainer, retainedMessagesManager);
|
||||
SubscriptionsManager = new MqttClientSubscriptionsManager(this, eventContainer, retainedMessagesManager);
|
||||
}
|
||||
|
||||
public event EventHandler Deleted;
|
||||
|
||||
|
||||
public string Id { get; }
|
||||
|
||||
/// <summary>
|
||||
@@ -54,11 +53,11 @@ namespace MQTTnet.Server
|
||||
|
||||
public MqttClientSubscriptionsManager SubscriptionsManager { get; }
|
||||
|
||||
public IDictionary<object, object> Items { get; }
|
||||
public IDictionary Items { get; }
|
||||
|
||||
public bool WillMessageSent { get; set; }
|
||||
|
||||
public long PendingDataPacketsCount => _packetBus.DataPacketsCount;
|
||||
public long PendingDataPacketsCount => _packetBus.PartitionItemsCount(MqttPacketBusPartition.Data);
|
||||
|
||||
public void AcknowledgePublishPacket(ushort packetIdentifier)
|
||||
{
|
||||
@@ -69,14 +68,16 @@ namespace MQTTnet.Server
|
||||
{
|
||||
if (packetBusItem == null) throw new ArgumentNullException(nameof(packetBusItem));
|
||||
|
||||
if (_packetBus.PacketsCount >= _serverOptions.MaxPendingMessagesPerClient)
|
||||
if (_packetBus.ItemsCount >= _serverOptions.MaxPendingMessagesPerClient)
|
||||
{
|
||||
if (_serverOptions.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropNewMessage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
if (_serverOptions.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage)
|
||||
{
|
||||
_packetBus.DropFirstItem(MqttPacketBusPartition.Data);
|
||||
|
||||
// TODO: Implement.
|
||||
}
|
||||
}
|
||||
@@ -88,33 +89,28 @@ namespace MQTTnet.Server
|
||||
_unacknowledgedPublishPackets[publishPacket.PacketIdentifier] = publishPacket;
|
||||
}
|
||||
|
||||
_packetBus.Enqueue(packetBusItem, MqttPacketBusPartition.Data);
|
||||
_packetBus.EnqueueItem(packetBusItem, MqttPacketBusPartition.Data);
|
||||
}
|
||||
else if (packetBusItem.Packet is MqttPingReqPacket || packetBusItem.Packet is MqttPingRespPacket)
|
||||
{
|
||||
_packetBus.Enqueue(packetBusItem, MqttPacketBusPartition.Health);
|
||||
_packetBus.EnqueueItem(packetBusItem, MqttPacketBusPartition.Health);
|
||||
}
|
||||
else
|
||||
{
|
||||
_packetBus.Enqueue(packetBusItem, MqttPacketBusPartition.Control);
|
||||
_packetBus.EnqueueItem(packetBusItem, MqttPacketBusPartition.Control);
|
||||
}
|
||||
}
|
||||
|
||||
public Task<MqttPacketBusItem> DequeuePacketAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return _packetBus.DequeueAsync(cancellationToken);
|
||||
return _packetBus.DequeueItemAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public Task DeleteAsync()
|
||||
{
|
||||
return _clientSessionsManager.DeleteSessionAsync(Id);
|
||||
}
|
||||
|
||||
public void OnDeleted()
|
||||
{
|
||||
Deleted?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
|
||||
public void Recover()
|
||||
{
|
||||
// TODO: Keep the bus and only insert pending items again.
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public enum MqttApplicationMessageResponseReasonCode
|
||||
{
|
||||
Success = 0,
|
||||
//NoMatchingSubscribers = 16,
|
||||
UnspecifiedError = 128,
|
||||
ImplementationSpecificError = 131,
|
||||
NotAuthorized = 135,
|
||||
TopicNameInvalid = 144,
|
||||
//PacketIdentifierInUse = 145,
|
||||
QuotaExceeded = 151,
|
||||
PayloadFormatInvalid = 153
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public sealed class MqttClientMessageQueueInterceptorContext
|
||||
{
|
||||
public string SenderClientId { get; internal set; }
|
||||
|
||||
public string ReceiverClientId { get; internal set; }
|
||||
|
||||
public MqttApplicationMessage ApplicationMessage { get; set; }
|
||||
|
||||
public bool AcceptEnqueue { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the supscription quality of service level.
|
||||
/// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message.
|
||||
/// There are 3 QoS levels in MQTT:
|
||||
/// - At most once (0): Message gets delivered no time, once or multiple times.
|
||||
/// - At least once (1): Message gets delivered at least once (one time or more often).
|
||||
/// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once).
|
||||
/// </summary>
|
||||
public MqttQualityOfServiceLevel SubscriptionQualityOfServiceLevel { get; set; }
|
||||
}
|
||||
}
|
||||
+230
-176
@@ -1,120 +1,43 @@
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Protocol;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public class MqttServer : Disposable
|
||||
{
|
||||
readonly MqttServerEventContainer _eventContainer = new MqttServerEventContainer();
|
||||
|
||||
readonly ICollection<IMqttServerAdapter> _adapters;
|
||||
readonly MqttServerEventContainer _eventContainer = new MqttServerEventContainer();
|
||||
readonly MqttNetSourceLogger _logger;
|
||||
readonly MqttServerOptions _options;
|
||||
readonly IMqttNetLogger _rootLogger;
|
||||
readonly MqttNetSourceLogger _logger;
|
||||
CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
MqttClientSessionsManager _clientSessionsManager;
|
||||
MqttRetainedMessagesManager _retainedMessagesManager;
|
||||
MqttServerKeepAliveMonitor _keepAliveMonitor;
|
||||
CancellationTokenSource _cancellationTokenSource;
|
||||
MqttRetainedMessagesManager _retainedMessagesManager;
|
||||
|
||||
public MqttServer(MqttServerOptions options, IEnumerable<IMqttServerAdapter> adapters, IMqttNetLogger logger)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
|
||||
if (adapters == null) throw new ArgumentNullException(nameof(adapters));
|
||||
|
||||
if (adapters == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(adapters));
|
||||
}
|
||||
|
||||
_adapters = adapters.ToList();
|
||||
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
_rootLogger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_logger = logger.WithSource(nameof(MqttServer));
|
||||
_rootLogger = logger;
|
||||
}
|
||||
|
||||
public event Func<EventArgs, Task> StartedAsync
|
||||
{
|
||||
add => _eventContainer.StartedEvent.AddHandler(value);
|
||||
remove => _eventContainer.StartedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<EventArgs, Task> StoppedAsync
|
||||
{
|
||||
add => _eventContainer.StoppedEvent.AddHandler(value);
|
||||
remove => _eventContainer.StoppedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<ValidatingConnectionEventArgs, Task> ValidatingConnectionAsync
|
||||
{
|
||||
add => _eventContainer.ValidatingConnectionEvent.AddHandler(value);
|
||||
remove => _eventContainer.ValidatingConnectionEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<ClientConnectedEventArgs, Task> ClientConnectedAsync
|
||||
{
|
||||
add => _eventContainer.ClientConnectedEvent.AddHandler(value);
|
||||
remove => _eventContainer.ClientConnectedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<ClientDisconnectedEventArgs, Task> ClientDisconnectedAsync
|
||||
{
|
||||
add => _eventContainer.ClientDisconnectedEvent.AddHandler(value);
|
||||
remove => _eventContainer.ClientDisconnectedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<InterceptingPacketEventArgs, Task> InterceptingInboundPacketAsync
|
||||
{
|
||||
add => _eventContainer.InterceptingInboundPacketEvent.AddHandler(value);
|
||||
remove => _eventContainer.InterceptingInboundPacketEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<InterceptingPacketEventArgs, Task> InterceptingOutboundPacketAsync
|
||||
{
|
||||
add => _eventContainer.InterceptingOutboundPacketEvent.AddHandler(value);
|
||||
remove => _eventContainer.InterceptingOutboundPacketEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<InterceptingSubscriptionEventArgs, Task> InterceptingSubscriptionAsync
|
||||
{
|
||||
add => _eventContainer.InterceptingSubscriptionEvent.AddHandler(value);
|
||||
remove => _eventContainer.InterceptingSubscriptionEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<InterceptingUnsubscriptionEventArgs, Task> InterceptingUnsubscriptionAsync
|
||||
{
|
||||
add => _eventContainer.InterceptingUnsubscriptionEvent.AddHandler(value);
|
||||
remove => _eventContainer.InterceptingUnsubscriptionEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<InterceptingPublishEventArgs, Task> InterceptingPublishAsync
|
||||
{
|
||||
add => _eventContainer.InterceptingPublishEvent.AddHandler(value);
|
||||
remove => _eventContainer.InterceptingPublishEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<ClientSubscribedTopicEventArgs, Task> ClientSubscribedTopicAsync
|
||||
{
|
||||
add => _eventContainer.ClientSubscribedTopicEvent.AddHandler(value);
|
||||
remove => _eventContainer.ClientSubscribedTopicEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<ClientUnsubscribedTopicEventArgs, Task> ClientUnsubscribedTopicAsync
|
||||
{
|
||||
add => _eventContainer.ClientUnsubscribedTopicEvent.AddHandler(value);
|
||||
remove => _eventContainer.ClientUnsubscribedTopicEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<PreparingSessionEventArgs, Task> PreparingSessionAsync
|
||||
{
|
||||
add => _eventContainer.PreparingSessionEvent.AddHandler(value);
|
||||
remove => _eventContainer.PreparingSessionEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<ApplicationMessageNotConsumedEventArgs, Task> ApplicationMessageNotConsumedAsync
|
||||
@@ -123,27 +46,123 @@ namespace MQTTnet.Server
|
||||
remove => _eventContainer.ApplicationMessageNotConsumedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<RetainedMessageChangedEventArgs, Task> RetainedMessageChangedAsync
|
||||
public event Func<ClientConnectedEventArgs, Task> ClientConnectedAsync
|
||||
{
|
||||
add => _eventContainer.RetainedMessageChangedEvent.AddHandler(value);
|
||||
remove => _eventContainer.RetainedMessageChangedEvent.RemoveHandler(value);
|
||||
add => _eventContainer.ClientConnectedEvent.AddHandler(value);
|
||||
remove => _eventContainer.ClientConnectedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
|
||||
public event Func<ClientDisconnectedEventArgs, Task> ClientDisconnectedAsync
|
||||
{
|
||||
add => _eventContainer.ClientDisconnectedEvent.AddHandler(value);
|
||||
remove => _eventContainer.ClientDisconnectedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<ClientSubscribedTopicEventArgs, Task> ClientSubscribedTopicAsync
|
||||
{
|
||||
add => _eventContainer.ClientSubscribedTopicEvent.AddHandler(value);
|
||||
remove => _eventContainer.ClientSubscribedTopicEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<ClientUnsubscribedTopicEventArgs, Task> ClientUnsubscribedTopicAsync
|
||||
{
|
||||
add => _eventContainer.ClientUnsubscribedTopicEvent.AddHandler(value);
|
||||
remove => _eventContainer.ClientUnsubscribedTopicEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<InterceptingPacketEventArgs, Task> InterceptingInboundPacketAsync
|
||||
{
|
||||
add => _eventContainer.InterceptingInboundPacketEvent.AddHandler(value);
|
||||
remove => _eventContainer.InterceptingInboundPacketEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<InterceptingPacketEventArgs, Task> InterceptingOutboundPacketAsync
|
||||
{
|
||||
add => _eventContainer.InterceptingOutboundPacketEvent.AddHandler(value);
|
||||
remove => _eventContainer.InterceptingOutboundPacketEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<InterceptingPublishEventArgs, Task> InterceptingPublishAsync
|
||||
{
|
||||
add => _eventContainer.InterceptingPublishEvent.AddHandler(value);
|
||||
remove => _eventContainer.InterceptingPublishEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<InterceptingSubscriptionEventArgs, Task> InterceptingSubscriptionAsync
|
||||
{
|
||||
add => _eventContainer.InterceptingSubscriptionEvent.AddHandler(value);
|
||||
remove => _eventContainer.InterceptingSubscriptionEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<InterceptingUnsubscriptionEventArgs, Task> InterceptingUnsubscriptionAsync
|
||||
{
|
||||
add => _eventContainer.InterceptingUnsubscriptionEvent.AddHandler(value);
|
||||
remove => _eventContainer.InterceptingUnsubscriptionEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<LoadingRetainedMessagesEventArgs, Task> LoadingRetainedMessageAsync
|
||||
{
|
||||
add => _eventContainer.LoadingRetainedMessagesEvent.AddHandler(value);
|
||||
remove => _eventContainer.LoadingRetainedMessagesEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
|
||||
public event Func<PreparingSessionEventArgs, Task> PreparingSessionAsync
|
||||
{
|
||||
add => _eventContainer.PreparingSessionEvent.AddHandler(value);
|
||||
remove => _eventContainer.PreparingSessionEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<RetainedMessageChangedEventArgs, Task> RetainedMessageChangedAsync
|
||||
{
|
||||
add => _eventContainer.RetainedMessageChangedEvent.AddHandler(value);
|
||||
remove => _eventContainer.RetainedMessageChangedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<EventArgs, Task> RetainedMessagesClearedAsync
|
||||
{
|
||||
add => _eventContainer.RetainedMessagesClearedEvent.AddHandler(value);
|
||||
remove => _eventContainer.RetainedMessagesClearedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<SessionDeletedEventArgs, Task> SessionDeletedAsync
|
||||
{
|
||||
add => _eventContainer.SessionDeletedEvent.AddHandler(value);
|
||||
remove => _eventContainer.SessionDeletedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<EventArgs, Task> StartedAsync
|
||||
{
|
||||
add => _eventContainer.StartedEvent.AddHandler(value);
|
||||
remove => _eventContainer.StartedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<EventArgs, Task> StoppedAsync
|
||||
{
|
||||
add => _eventContainer.StoppedEvent.AddHandler(value);
|
||||
remove => _eventContainer.StoppedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<ValidatingConnectionEventArgs, Task> ValidatingConnectionAsync
|
||||
{
|
||||
add => _eventContainer.ValidatingConnectionEvent.AddHandler(value);
|
||||
remove => _eventContainer.ValidatingConnectionEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public bool IsStarted => _cancellationTokenSource != null;
|
||||
|
||||
public Task<IList<IMqttClientStatus>> GetClientsAsync()
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all retained messages.
|
||||
/// </summary>
|
||||
/// <returns>A task.</returns>
|
||||
public Task DeleteRetainedMessagesAsync()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotStarted();
|
||||
|
||||
return _retainedMessagesManager?.ClearMessages() ?? PlatformAbstractionLayer.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<IList<MqttClientStatus>> GetClientsAsync()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotStarted();
|
||||
@@ -151,16 +170,8 @@ namespace MQTTnet.Server
|
||||
return _clientSessionsManager.GetClientStatusAsync();
|
||||
}
|
||||
|
||||
public Task<IList<IMqttSessionStatus>> GetSessionsAsync()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotStarted();
|
||||
|
||||
return _clientSessionsManager.GetSessionStatusAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all currently cached retained messages.
|
||||
/// Gets all currently cached retained messages.
|
||||
/// </summary>
|
||||
/// <returns>A list of retained messages.</returns>
|
||||
public Task<IList<MqttApplicationMessage>> GetRetainedMessagesAsync()
|
||||
@@ -172,80 +183,63 @@ namespace MQTTnet.Server
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all retained messages.
|
||||
/// Disconnects the client with the specified ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID.</param>
|
||||
/// <param name="reasonCode">The reason code.</param>
|
||||
/// <returns>A task.</returns>
|
||||
public Task DeleteRetainedMessagesAsync()
|
||||
public Task DisconnectClientAsync(string id, MqttDisconnectReasonCode reasonCode)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotStarted();
|
||||
|
||||
return _retainedMessagesManager?.ClearMessages() ?? PlatformAbstractionLayer.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the specified retained message.
|
||||
/// Setting the payload to null or empty will delete the retained message.
|
||||
/// </summary>
|
||||
/// <param name="retainedMessage">The retained message.</param>
|
||||
/// <returns>A task.</returns>
|
||||
public Task UpdateRetainedMessageAsync(MqttApplicationMessage retainedMessage)
|
||||
{
|
||||
if (retainedMessage == null) throw new ArgumentNullException(nameof(retainedMessage));
|
||||
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotStarted();
|
||||
|
||||
return _retainedMessagesManager?.UpdateMessage(null, retainedMessage);
|
||||
}
|
||||
|
||||
public Task SubscribeAsync(string clientId, ICollection<MqttTopicFilter> topicFilters)
|
||||
{
|
||||
if (clientId == null) throw new ArgumentNullException(nameof(clientId));
|
||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
||||
|
||||
foreach (var topicFilter in topicFilters)
|
||||
// TODO: Add more options.
|
||||
if (id == null)
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalidSubscribe(topicFilter.Topic);
|
||||
throw new ArgumentNullException(nameof(id));
|
||||
}
|
||||
|
||||
return _clientSessionsManager.GetClient(id).StopAsync(reasonCode);
|
||||
}
|
||||
|
||||
public Task<IList<MqttSessionStatus>> GetSessionsAsync()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotStarted();
|
||||
|
||||
return _clientSessionsManager.GetSessionStatusAsync();
|
||||
}
|
||||
|
||||
public sealed class MqttInjectedApplicationMessage
|
||||
{
|
||||
public string SenderClientId { get; set; }
|
||||
|
||||
public MqttApplicationMessage ApplicationMessage { get; set; }
|
||||
}
|
||||
|
||||
public Task InjectApplicationMessage(MqttInjectedApplicationMessage injectedApplicationMessage, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (injectedApplicationMessage == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(injectedApplicationMessage));
|
||||
}
|
||||
|
||||
if (injectedApplicationMessage.ApplicationMessage == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(injectedApplicationMessage.ApplicationMessage));
|
||||
}
|
||||
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotStarted();
|
||||
|
||||
return _clientSessionsManager.SubscribeAsync(clientId, topicFilters);
|
||||
}
|
||||
|
||||
public Task UnsubscribeAsync(string clientId, ICollection<string> topicFilters)
|
||||
{
|
||||
if (clientId == null) throw new ArgumentNullException(nameof(clientId));
|
||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
||||
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotStarted();
|
||||
|
||||
return _clientSessionsManager.UnsubscribeAsync(clientId, topicFilters);
|
||||
}
|
||||
|
||||
public async Task<MqttClientPublishResult> PublishAsync(string senderClientId, MqttApplicationMessage applicationMessage, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
ThrowIfDisposed();
|
||||
|
||||
MqttTopicValidator.ThrowIfInvalid(applicationMessage.Topic);
|
||||
MqttTopicValidator.ThrowIfInvalid(injectedApplicationMessage.ApplicationMessage.Topic);
|
||||
|
||||
ThrowIfNotStarted();
|
||||
|
||||
await _clientSessionsManager.DispatchPublishPacket(senderClientId, applicationMessage);
|
||||
|
||||
return new MqttClientPublishResult();
|
||||
return _clientSessionsManager.DispatchPublishPacket(injectedApplicationMessage.SenderClientId, injectedApplicationMessage.ApplicationMessage);
|
||||
}
|
||||
|
||||
|
||||
public async Task StartAsync()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfStarted();
|
||||
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
var cancellationToken = _cancellationTokenSource.Token;
|
||||
|
||||
@@ -253,7 +247,7 @@ namespace MQTTnet.Server
|
||||
await _retainedMessagesManager.LoadMessages().ConfigureAwait(false);
|
||||
|
||||
_clientSessionsManager = new MqttClientSessionsManager(_options, _retainedMessagesManager, _eventContainer, _rootLogger);
|
||||
|
||||
|
||||
_keepAliveMonitor = new MqttServerKeepAliveMonitor(_options, _clientSessionsManager, _rootLogger);
|
||||
_keepAliveMonitor.Start(cancellationToken);
|
||||
|
||||
@@ -280,7 +274,7 @@ namespace MQTTnet.Server
|
||||
_cancellationTokenSource.Cancel(false);
|
||||
|
||||
await _clientSessionsManager.CloseAllConnectionsAsync().ConfigureAwait(false);
|
||||
|
||||
|
||||
foreach (var adapter in _adapters)
|
||||
{
|
||||
adapter.ClientHandler = null;
|
||||
@@ -303,6 +297,66 @@ namespace MQTTnet.Server
|
||||
await _eventContainer.StoppedEvent.InvokeAsync(EventArgs.Empty).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public Task SubscribeAsync(string clientId, ICollection<MqttTopicFilter> topicFilters)
|
||||
{
|
||||
if (clientId == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(clientId));
|
||||
}
|
||||
|
||||
if (topicFilters == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(topicFilters));
|
||||
}
|
||||
|
||||
foreach (var topicFilter in topicFilters)
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalidSubscribe(topicFilter.Topic);
|
||||
}
|
||||
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotStarted();
|
||||
|
||||
return _clientSessionsManager.SubscribeAsync(clientId, topicFilters);
|
||||
}
|
||||
|
||||
public Task UnsubscribeAsync(string clientId, ICollection<string> topicFilters)
|
||||
{
|
||||
if (clientId == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(clientId));
|
||||
}
|
||||
|
||||
if (topicFilters == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(topicFilters));
|
||||
}
|
||||
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotStarted();
|
||||
|
||||
return _clientSessionsManager.UnsubscribeAsync(clientId, topicFilters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the specified retained message.
|
||||
/// Setting the payload to null or empty will delete the retained message.
|
||||
/// </summary>
|
||||
/// <param name="retainedMessage">The retained message.</param>
|
||||
/// <returns>A task.</returns>
|
||||
public Task UpdateRetainedMessageAsync(MqttApplicationMessage retainedMessage)
|
||||
{
|
||||
if (retainedMessage == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(retainedMessage));
|
||||
}
|
||||
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotStarted();
|
||||
|
||||
return _retainedMessagesManager?.UpdateMessage(null, retainedMessage);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
@@ -323,14 +377,6 @@ namespace MQTTnet.Server
|
||||
return _clientSessionsManager.HandleClientConnectionAsync(channelAdapter, cancellationToken);
|
||||
}
|
||||
|
||||
void ThrowIfStarted()
|
||||
{
|
||||
if (_cancellationTokenSource != null)
|
||||
{
|
||||
throw new InvalidOperationException("The MQTT server is already started.");
|
||||
}
|
||||
}
|
||||
|
||||
void ThrowIfNotStarted()
|
||||
{
|
||||
if (_cancellationTokenSource == null)
|
||||
@@ -338,5 +384,13 @@ namespace MQTTnet.Server
|
||||
throw new InvalidOperationException("The MQTT server is not started.");
|
||||
}
|
||||
}
|
||||
|
||||
void ThrowIfStarted()
|
||||
{
|
||||
if (_cancellationTokenSource != null)
|
||||
{
|
||||
throw new InvalidOperationException("The MQTT server is already started.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public sealed class PublishResponse
|
||||
{
|
||||
public MqttApplicationMessageResponseReasonCode ReasonCode { get; set; } = MqttApplicationMessageResponseReasonCode.Success;
|
||||
public MqttPubAckReasonCode ReasonCode { get; set; } = MqttPubAckReasonCode.Success;
|
||||
|
||||
public string ReasonString { get; set; }
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public interface IMqttClientStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the client identifier.
|
||||
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
|
||||
/// </summary>
|
||||
string ClientId { get; }
|
||||
|
||||
string Endpoint { get; }
|
||||
|
||||
MqttProtocolVersion ProtocolVersion { get; }
|
||||
|
||||
DateTime ConnectedTimestamp { get; }
|
||||
|
||||
DateTime LastPacketReceivedTimestamp { get; }
|
||||
|
||||
DateTime LastPacketSentTimestamp { get; }
|
||||
|
||||
DateTime LastNonKeepAlivePacketReceivedTimestamp { get; }
|
||||
|
||||
long ReceivedApplicationMessagesCount { get; }
|
||||
|
||||
long SentApplicationMessagesCount { get; }
|
||||
|
||||
long ReceivedPacketsCount { get; }
|
||||
|
||||
long SentPacketsCount { get; }
|
||||
|
||||
IMqttSessionStatus Session { get; }
|
||||
|
||||
long BytesSent { get; }
|
||||
|
||||
long BytesReceived { get; }
|
||||
|
||||
Task DisconnectAsync();
|
||||
|
||||
void ResetStatistics();
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public interface IMqttSessionStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Fired when this session is being deleted.
|
||||
/// </summary>
|
||||
event EventHandler Deleted;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the client identifier.
|
||||
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
|
||||
/// </summary>
|
||||
string ClientId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the count of messages which are not yet sent to the client but already queued.
|
||||
/// </summary>
|
||||
long PendingApplicationMessagesCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This items can be used by the library user in order to store custom information.
|
||||
/// </summary>
|
||||
IDictionary<object, object> Items { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues a new application message.
|
||||
/// </summary>
|
||||
/// <param name="applicationMessage">The application message.</param>
|
||||
/// <returns>A task.</returns>
|
||||
Task EnqueueApplicationMessageAsync(MqttApplicationMessage applicationMessage);
|
||||
|
||||
/// <summary>
|
||||
/// Delivers a new application message.
|
||||
/// </summary>
|
||||
/// <param name="applicationMessage">The application message.</param>
|
||||
/// <returns>A task.</returns>
|
||||
Task DeliverApplicationMessageAsync(MqttApplicationMessage applicationMessage);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the entire queue with pending application messages.
|
||||
/// </summary>
|
||||
/// <returns>A task.</returns>
|
||||
Task ClearApplicationMessagesQueueAsync();
|
||||
|
||||
[Obsolete("Please use _ClearApplicationMessagesQueueAsync_ instead. This will be removed soon.")]
|
||||
Task ClearPendingApplicationMessagesAsync();
|
||||
|
||||
Task DeleteAsync();
|
||||
}
|
||||
}
|
||||
@@ -1,59 +1,59 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public sealed class MqttClientStatus : IMqttClientStatus
|
||||
public sealed class MqttClientStatus
|
||||
{
|
||||
readonly MqttClient _connection;
|
||||
readonly MqttClient _client;
|
||||
|
||||
public MqttClientStatus(MqttClient connection)
|
||||
public MqttClientStatus(MqttClient client)
|
||||
{
|
||||
_connection = connection ?? throw new ArgumentNullException(nameof(connection));
|
||||
_client = client ?? throw new ArgumentNullException(nameof(client));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the client identifier.
|
||||
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
|
||||
/// </summary>
|
||||
public string ClientId => _connection.Id;
|
||||
public string Id => _client.Id;
|
||||
|
||||
public string Endpoint => _connection.Endpoint;
|
||||
public string Endpoint => _client.Endpoint;
|
||||
|
||||
public MqttProtocolVersion ProtocolVersion => _connection.ChannelAdapter.PacketFormatterAdapter.ProtocolVersion;
|
||||
public MqttProtocolVersion ProtocolVersion => _client.ChannelAdapter.PacketFormatterAdapter.ProtocolVersion;
|
||||
|
||||
public DateTime ConnectedTimestamp => _connection.Statistics.ConnectedTimestamp;
|
||||
public DateTime ConnectedTimestamp => _client.Statistics.ConnectedTimestamp;
|
||||
|
||||
public DateTime LastPacketReceivedTimestamp => _connection.Statistics.LastPacketReceivedTimestamp;
|
||||
public DateTime LastPacketReceivedTimestamp => _client.Statistics.LastPacketReceivedTimestamp;
|
||||
|
||||
public DateTime LastPacketSentTimestamp => _connection.Statistics.LastPacketSentTimestamp;
|
||||
public DateTime LastPacketSentTimestamp => _client.Statistics.LastPacketSentTimestamp;
|
||||
|
||||
public DateTime LastNonKeepAlivePacketReceivedTimestamp => _connection.Statistics.LastNonKeepAlivePacketReceivedTimestamp;
|
||||
public DateTime LastNonKeepAlivePacketReceivedTimestamp => _client.Statistics.LastNonKeepAlivePacketReceivedTimestamp;
|
||||
|
||||
public long ReceivedApplicationMessagesCount => _connection.Statistics.ReceivedApplicationMessagesCount;
|
||||
public long ReceivedApplicationMessagesCount => _client.Statistics.ReceivedApplicationMessagesCount;
|
||||
|
||||
public long SentApplicationMessagesCount => _connection.Statistics.SentApplicationMessagesCount;
|
||||
public long SentApplicationMessagesCount => _client.Statistics.SentApplicationMessagesCount;
|
||||
|
||||
public long ReceivedPacketsCount => _connection.Statistics.ReceivedPacketsCount;
|
||||
public long ReceivedPacketsCount => _client.Statistics.ReceivedPacketsCount;
|
||||
|
||||
public long SentPacketsCount => _connection.Statistics.SentPacketsCount;
|
||||
public long SentPacketsCount => _client.Statistics.SentPacketsCount;
|
||||
|
||||
public IMqttSessionStatus Session { get; set; }
|
||||
public MqttSessionStatus Session { get; set; }
|
||||
|
||||
public long BytesSent => _connection.ChannelAdapter.BytesSent;
|
||||
public long BytesSent => _client.ChannelAdapter.BytesSent;
|
||||
|
||||
public long BytesReceived => _connection.ChannelAdapter.BytesReceived;
|
||||
public long BytesReceived => _client.ChannelAdapter.BytesReceived;
|
||||
|
||||
public Task DisconnectAsync()
|
||||
{
|
||||
return _connection.StopAsync(MqttClientDisconnectReason.NormalDisconnection);
|
||||
return _client.StopAsync(MqttDisconnectReasonCode.NormalDisconnection);
|
||||
}
|
||||
|
||||
public void ResetStatistics()
|
||||
{
|
||||
_connection.ResetStatistics();
|
||||
_client.ResetStatistics();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
@@ -7,7 +7,7 @@ using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public sealed class MqttSessionStatus : IMqttSessionStatus
|
||||
public sealed class MqttSessionStatus
|
||||
{
|
||||
readonly MqttSession _session;
|
||||
|
||||
@@ -16,33 +16,18 @@ namespace MQTTnet.Server
|
||||
_session = session ?? throw new ArgumentNullException(nameof(session));
|
||||
}
|
||||
|
||||
public event EventHandler Deleted
|
||||
{
|
||||
add => _session.Deleted += value;
|
||||
remove => _session.Deleted += value;
|
||||
}
|
||||
|
||||
public string ClientId => _session.Id;
|
||||
public string Id => _session.Id;
|
||||
|
||||
public long PendingApplicationMessagesCount => _session.PendingDataPacketsCount;
|
||||
|
||||
public DateTime CreatedTimestamp => _session.CreatedTimestamp;
|
||||
|
||||
public IDictionary<object, object> Items => _session.Items;
|
||||
public IDictionary Items => _session.Items;
|
||||
|
||||
public Task EnqueueApplicationMessageAsync(MqttApplicationMessage applicationMessage)
|
||||
{
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
// _session.ApplicationMessagesQueue.Enqueue(new MqttQueuedApplicationMessage
|
||||
// {
|
||||
// ApplicationMessage = applicationMessage,
|
||||
// IsDuplicate = false,
|
||||
// IsRetainedMessage = false,
|
||||
// SubscriptionQualityOfServiceLevel = applicationMessage.QualityOfServiceLevel,
|
||||
// SenderClientId = null
|
||||
// });
|
||||
|
||||
var publishPacketFactory = new MqttPublishPacketFactory();
|
||||
_session.EnqueuePacket(new MqttPacketBusItem(publishPacketFactory.Create(applicationMessage)));
|
||||
|
||||
@@ -59,22 +44,15 @@ namespace MQTTnet.Server
|
||||
|
||||
return packetBusItem.WaitForDeliveryAsync();
|
||||
}
|
||||
|
||||
public Task ClearApplicationMessagesQueueAsync()
|
||||
{
|
||||
// TODO: Fix!
|
||||
//_session.ApplicationMessagesQueue.Clear();
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
|
||||
public Task DeleteAsync()
|
||||
{
|
||||
return _session.DeleteAsync();
|
||||
}
|
||||
|
||||
public Task ClearPendingApplicationMessagesAsync()
|
||||
public Task ClearApplicationMessagesQueueAsync()
|
||||
{
|
||||
return ClearApplicationMessagesQueueAsync();
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Server;
|
||||
using IMqttClient = MQTTnet.Client.IMqttClient;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@ using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Extensions.ManagedClient;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
using IMqttClient = MQTTnet.Client.IMqttClient;
|
||||
|
||||
namespace MQTTnet.Tests.Client
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace MQTTnet.Tests.Internal
|
||||
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(1)))
|
||||
{
|
||||
await bus.DequeueAsync(timeout.Token);
|
||||
await bus.DequeueItemAsync(timeout.Token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,31 +27,31 @@ namespace MQTTnet.Tests.Internal
|
||||
{
|
||||
var bus = new MqttPacketBus();
|
||||
|
||||
bus.Enqueue(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.Enqueue(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.Enqueue(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
|
||||
bus.Enqueue(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||
bus.Enqueue(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||
bus.Enqueue(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||
|
||||
bus.Enqueue(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||
bus.Enqueue(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||
bus.Enqueue(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||
|
||||
Assert.AreEqual(9, bus.PacketsCount);
|
||||
Assert.AreEqual(9, bus.ItemsCount);
|
||||
|
||||
Assert.IsInstanceOfType(bus.DequeueAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
||||
|
||||
Assert.AreEqual(0, bus.PacketsCount);
|
||||
Assert.AreEqual(0, bus.ItemsCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -59,11 +59,11 @@ namespace MQTTnet.Tests.Internal
|
||||
{
|
||||
var bus = new MqttPacketBus();
|
||||
|
||||
bus.Enqueue(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.Enqueue(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.Enqueue(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
|
||||
Assert.AreEqual(3, bus.PacketsCount);
|
||||
Assert.AreEqual(3, bus.ItemsCount);
|
||||
|
||||
var exportedPackets = bus.ExportPackets(MqttPacketBusPartition.Control);
|
||||
Assert.AreEqual(0, exportedPackets.Count);
|
||||
@@ -74,7 +74,7 @@ namespace MQTTnet.Tests.Internal
|
||||
exportedPackets = bus.ExportPackets(MqttPacketBusPartition.Data);
|
||||
Assert.AreEqual(3, exportedPackets.Count);
|
||||
|
||||
Assert.AreEqual(3, bus.PacketsCount);
|
||||
Assert.AreEqual(3, bus.ItemsCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -93,21 +93,21 @@ namespace MQTTnet.Tests.Internal
|
||||
delivered = true;
|
||||
};
|
||||
|
||||
bus.Enqueue(item1, MqttPacketBusPartition.Data);
|
||||
bus.Enqueue(item2, MqttPacketBusPartition.Data);
|
||||
bus.Enqueue(item3, MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(item1, MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(item2, MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(item3, MqttPacketBusPartition.Data);
|
||||
|
||||
Assert.IsFalse(delivered);
|
||||
|
||||
bus.DequeueAsync(CancellationToken.None).Result.MarkAsDelivered();
|
||||
bus.DequeueItemAsync(CancellationToken.None).Result.MarkAsDelivered();
|
||||
|
||||
Assert.IsFalse(delivered);
|
||||
|
||||
bus.DequeueAsync(CancellationToken.None).Result.MarkAsDelivered();
|
||||
bus.DequeueItemAsync(CancellationToken.None).Result.MarkAsDelivered();
|
||||
|
||||
Assert.IsFalse(delivered);
|
||||
|
||||
bus.DequeueAsync(CancellationToken.None).Result.MarkAsDelivered();
|
||||
bus.DequeueItemAsync(CancellationToken.None).Result.MarkAsDelivered();
|
||||
|
||||
// The third packet has the event attached.
|
||||
Assert.IsTrue(delivered);
|
||||
|
||||
@@ -12,6 +12,7 @@ using MQTTnet.Extensions.Rpc;
|
||||
using MQTTnet.Extensions.Rpc.Options;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.LowLevelClient;
|
||||
using IMqttClient = MQTTnet.Client.IMqttClient;
|
||||
|
||||
namespace MQTTnet.Tests.Mockups
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -32,7 +32,7 @@ namespace MQTTnet.Tests.Server
|
||||
retainedMessagesManager,
|
||||
new MqttClientSessionsManager(options, retainedMessagesManager, eventContainer, logger));
|
||||
|
||||
_subscriptionsManager = new MqttClientSubscriptionsManager(session, new MqttServerOptions(), new MqttServerEventContainer(),
|
||||
_subscriptionsManager = new MqttClientSubscriptionsManager(session, new MqttServerEventContainer(),
|
||||
new MqttRetainedMessagesManager(new MqttServerEventContainer(), new MqttNetNullLogger()));
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using MQTTnet.Client;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
using IMqttClient = MQTTnet.Client.IMqttClient;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
@@ -124,18 +125,16 @@ namespace MQTTnet.Tests.Server
|
||||
{
|
||||
// Arrange client and server.
|
||||
var server = await testEnvironment.StartServer(o => o.WithPersistentSessions(false));
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
// Arrange session status tracking.
|
||||
var status = await server.GetSessionsAsync();
|
||||
var clientStatus = status[0];
|
||||
|
||||
|
||||
var deletedEventFired = false;
|
||||
clientStatus.Deleted += (_, __) =>
|
||||
server.SessionDeletedAsync += e =>
|
||||
{
|
||||
deletedEventFired = true;
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
// Act: Disconnect the client -> Event must be fired.
|
||||
await client.DisconnectAsync();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
@@ -29,8 +29,8 @@ namespace MQTTnet.Tests.Server
|
||||
Assert.AreEqual(2, clientStatus.Count);
|
||||
Assert.AreEqual(2, sessionStatus.Count);
|
||||
|
||||
Assert.IsTrue(clientStatus.Any(s => s.ClientId == c1.Options.ClientId));
|
||||
Assert.IsTrue(clientStatus.Any(s => s.ClientId == c2.Options.ClientId));
|
||||
Assert.IsTrue(clientStatus.Any(s => s.Id == c1.Options.ClientId));
|
||||
Assert.IsTrue(clientStatus.Any(s => s.Id == c2.Options.ClientId));
|
||||
|
||||
await c1.DisconnectAsync();
|
||||
await c2.DisconnectAsync();
|
||||
@@ -59,7 +59,7 @@ namespace MQTTnet.Tests.Server
|
||||
var clientStatus = await server.GetClientsAsync();
|
||||
|
||||
Assert.AreEqual(1, clientStatus.Count);
|
||||
Assert.IsTrue(clientStatus.Any(s => s.ClientId == c1.Options.ClientId));
|
||||
Assert.IsTrue(clientStatus.Any(s => s.Id == c1.Options.ClientId));
|
||||
|
||||
await clientStatus.First().DisconnectAsync();
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using MQTTnet.AspNetCore;
|
||||
using MQTTnet.AspNetCore.Extensions;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.TestApp.AspNetCore2
|
||||
{
|
||||
@@ -55,7 +56,12 @@ namespace MQTTnet.TestApp.AspNetCore2
|
||||
{
|
||||
try
|
||||
{
|
||||
await server.PublishAsync("server", msg.Build());
|
||||
await server.InjectApplicationMessage(new MqttServer.MqttInjectedApplicationMessage
|
||||
{
|
||||
SenderClientId = "server",
|
||||
ApplicationMessage = msg.Build()
|
||||
});
|
||||
|
||||
msg.WithPayload($"Mqtt hosted on {frameworkName} is still awesome at {DateTime.Now}");
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
using IMqttClient = MQTTnet.Client.IMqttClient;
|
||||
|
||||
namespace MQTTnet.TestApp.NetCore
|
||||
{
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace MQTTnet.TestApp.NetCore
|
||||
retainedMessages = new List<MqttApplicationMessage>();
|
||||
}
|
||||
|
||||
e.StoredRetainedMessages = retainedMessages;
|
||||
e.LoadedRetainedMessages = retainedMessages;
|
||||
|
||||
return Task.FromResult(0);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user