Migrate retained messages to the event based approach.

This commit is contained in:
Christian
2021-11-01 11:20:46 +01:00
parent 9ab85b3592
commit 22ccdf10c6
57 changed files with 451 additions and 666 deletions
@@ -6,7 +6,7 @@ namespace MQTTnet.Formatter
{ {
public sealed class MqttConnAckPacketFactory public sealed class MqttConnAckPacketFactory
{ {
public MqttConnAckPacket Create(ValidatingMqttClientConnectionEventArgs clientConnectionValidationEventArgs) public MqttConnAckPacket Create(ValidatingConnectionEventArgs clientConnectionValidationEventArgs)
{ {
if (clientConnectionValidationEventArgs == null) throw new ArgumentNullException(nameof(clientConnectionValidationEventArgs)); if (clientConnectionValidationEventArgs == null) throw new ArgumentNullException(nameof(clientConnectionValidationEventArgs));
@@ -8,7 +8,7 @@ namespace MQTTnet.Formatter
{ {
public sealed class MqttPubAckPacketFactory public sealed class MqttPubAckPacketFactory
{ {
public MqttPubAckPacket Create(MqttPublishPacket publishPacket, MqttApplicationMessageResponse applicationMessageResponse) public MqttPubAckPacket Create(MqttPublishPacket publishPacket, PublishResponse applicationMessageResponse)
{ {
if (applicationMessageResponse == null) throw new ArgumentNullException(nameof(applicationMessageResponse)); if (applicationMessageResponse == null) throw new ArgumentNullException(nameof(applicationMessageResponse));
@@ -18,7 +18,7 @@ namespace MQTTnet.Formatter
return pubRecPacket; return pubRecPacket;
} }
public MqttBasePacket Create(MqttPublishPacket publishPacket, MqttApplicationMessageResponse applicationMessageResponse) public MqttBasePacket Create(MqttPublishPacket publishPacket, PublishResponse applicationMessageResponse)
{ {
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket)); if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
@@ -6,17 +6,17 @@ namespace MQTTnet.Formatter
{ {
public sealed class MqttSubAckPacketFactory public sealed class MqttSubAckPacketFactory
{ {
public MqttSubAckPacket Create(MqttSubscribePacket subscribePacket, MqttSubscribeResult mqttSubscribeResult) public MqttSubAckPacket Create(MqttSubscribePacket subscribePacket, SubscribeResult subscribeResult)
{ {
if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket)); if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
if (mqttSubscribeResult == null) throw new ArgumentNullException(nameof(mqttSubscribeResult)); if (subscribeResult == null) throw new ArgumentNullException(nameof(subscribeResult));
var subAckPacket = new MqttSubAckPacket var subAckPacket = new MqttSubAckPacket
{ {
PacketIdentifier = subscribePacket.PacketIdentifier PacketIdentifier = subscribePacket.PacketIdentifier
}; };
subAckPacket.ReasonCodes.AddRange(mqttSubscribeResult.ReasonCodes); subAckPacket.ReasonCodes.AddRange(subscribeResult.ReasonCodes);
return subAckPacket; return subAckPacket;
} }
+1
View File
@@ -7,6 +7,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using MQTTnet.Diagnostics; using MQTTnet.Diagnostics;
using MqttClient = MQTTnet.Client.MqttClient;
namespace MQTTnet namespace MQTTnet
{ {
@@ -2,7 +2,7 @@
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
public sealed class MqttApplicationMessageNotConsumedEventArgs : EventArgs public sealed class ApplicationMessageNotConsumedEventArgs : EventArgs
{ {
/// <summary> /// <summary>
/// Gets the application message which was not consumed by any client. /// Gets the application message which was not consumed by any client.
@@ -3,7 +3,7 @@ using MQTTnet.Formatter;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
public sealed class MqttServerClientConnectedEventArgs : EventArgs public sealed class ClientConnectedEventArgs : EventArgs
{ {
/// <summary> /// <summary>
/// Gets the client identifier of the connected client. /// Gets the client identifier of the connected client.
@@ -2,7 +2,7 @@ using System;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
public sealed class MqttServerClientDisconnectedEventArgs : EventArgs public sealed class ClientDisconnectedEventArgs : EventArgs
{ {
/// <summary> /// <summary>
/// Gets the client identifier. /// Gets the client identifier.
@@ -3,7 +3,7 @@ using MQTTnet.Packets;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
public sealed class MqttServerClientSubscribedTopicEventArgs : EventArgs public sealed class ClientSubscribedTopicEventArgs : EventArgs
{ {
/// <summary> /// <summary>
/// Gets the client identifier. /// Gets the client identifier.
@@ -2,7 +2,7 @@
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
public sealed class MqttServerClientUnsubscribedTopicEventArgs : EventArgs public sealed class ClientUnsubscribedTopicEventArgs : EventArgs
{ {
/// <summary> /// <summary>
/// Gets the client identifier. /// Gets the client identifier.
@@ -4,7 +4,7 @@ using System.Threading;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
public sealed class InterceptingMqttClientPublishEventArgs : EventArgs public sealed class InterceptingPublishEventArgs : EventArgs
{ {
/// <summary> /// <summary>
/// Gets the client identifier. /// Gets the client identifier.
@@ -22,7 +22,7 @@ namespace MQTTnet.Server
/// <summary> /// <summary>
/// Gets the response which will be sent to the client via the PUBACK etc. packets. /// Gets the response which will be sent to the client via the PUBACK etc. packets.
/// </summary> /// </summary>
public MqttApplicationMessageResponse Response { get; } = new MqttApplicationMessageResponse(); public PublishResponse Response { get; } = new PublishResponse();
/// <summary> /// <summary>
/// Gets or sets whether the publish should be processed internally. /// Gets or sets whether the publish should be processed internally.
@@ -5,7 +5,7 @@ using MQTTnet.Packets;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
public sealed class InterceptingMqttClientSubscriptionEventArgs : EventArgs public sealed class InterceptingSubscriptionEventArgs : EventArgs
{ {
/// <summary> /// <summary>
/// Gets the client identifier. /// Gets the client identifier.
@@ -44,7 +44,7 @@ namespace MQTTnet.Server
/// <summary> /// <summary>
/// Gets the response which will be sent to the client via the SUBACK packet. /// Gets the response which will be sent to the client via the SUBACK packet.
/// </summary> /// </summary>
public MqttSubscribeResponse Response { get; } = new MqttSubscribeResponse(); public SubscribeResponse Response { get; } = new SubscribeResponse();
/// <summary> /// <summary>
/// Gets the cancellation token which can indicate that the client connection gets down. /// Gets the cancellation token which can indicate that the client connection gets down.
@@ -4,7 +4,7 @@ using System.Threading;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
public sealed class InterceptingMqttClientUnsubscriptionEventArgs : EventArgs public sealed class InterceptingUnsubscriptionEventArgs : EventArgs
{ {
/// <summary> /// <summary>
/// Gets the client identifier. /// Gets the client identifier.
@@ -27,7 +27,7 @@ namespace MQTTnet.Server
/// <summary> /// <summary>
/// Gets the response which will be sent to the client via the UNSUBACK pocket. /// Gets the response which will be sent to the client via the UNSUBACK pocket.
/// </summary> /// </summary>
public MqttUnsubscribeResponse Response { get; } = new MqttUnsubscribeResponse(); public UnsubscribeResponse Response { get; } = new UnsubscribeResponse();
/// <summary> /// <summary>
/// Gets or sets whether the broker should remove an internal subscription for the client. /// Gets or sets whether the broker should remove an internal subscription for the client.
@@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
namespace MQTTnet.Server
{
public sealed class LoadingRetainedMessagesEventArgs : EventArgs
{
public List<MqttApplicationMessage> StoredRetainedMessages { get; set; } = new List<MqttApplicationMessage>();
}
}
@@ -4,9 +4,9 @@ using MQTTnet.Packets;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
public sealed class PreparingMqttClientSessionEventArgs : EventArgs public sealed class PreparingSessionEventArgs : EventArgs
{ {
public string ClientId { get; internal set; } public string Id { get; internal set; }
// TODO: Allow adding of packets to the queue etc. // TODO: Allow adding of packets to the queue etc.
@@ -25,5 +25,21 @@ namespace MQTTnet.Server
public List<MqttSubscription> Subscriptions { get; } = new List<MqttSubscription>(); public List<MqttSubscription> Subscriptions { get; } = new List<MqttSubscription>();
public List<MqttPublishPacket> PublishPackets { get; } = new List<MqttPublishPacket>(); public List<MqttPublishPacket> PublishPackets { get; } = new List<MqttPublishPacket>();
IDictionary<object, object> Items { get; set; }
/// <summary>
/// Gets the last will message.
/// In MQTT, you use the last will message feature to notify other clients about an ungracefully disconnected client.
/// </summary>
MqttApplicationMessage WillMessage { get; set; }
/// <summary>
/// Gets the will delay interval.
/// This is the time between the client disconnect and the time the will message will be sent.
/// </summary>
uint? WillDelayInterval { get; set; }
DateTime? SessionExpiryTimestamp { get; set; }
} }
} }
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
namespace MQTTnet.Server
{
public sealed class RetainedMessageChangedEventArgs : EventArgs
{
public string ClientId { get; internal set; }
public MqttApplicationMessage ChangedRetainedMessage { get; internal set; }
public List<MqttApplicationMessage> StoredRetainedMessages { get; internal set; }
}
}
@@ -10,12 +10,12 @@ using MQTTnet.Protocol;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
public sealed class ValidatingMqttClientConnectionEventArgs : EventArgs public sealed class ValidatingConnectionEventArgs : EventArgs
{ {
readonly MqttConnectPacket _connectPacket; readonly MqttConnectPacket _connectPacket;
readonly IMqttChannelAdapter _clientAdapter; readonly IMqttChannelAdapter _clientAdapter;
public ValidatingMqttClientConnectionEventArgs(MqttConnectPacket connectPacket, IMqttChannelAdapter clientAdapter) public ValidatingConnectionEventArgs(MqttConnectPacket connectPacket, IMqttChannelAdapter clientAdapter)
{ {
_connectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket)); _connectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket));
_clientAdapter = clientAdapter ?? throw new ArgumentNullException(nameof(clientAdapter)); _clientAdapter = clientAdapter ?? throw new ArgumentNullException(nameof(clientAdapter));
@@ -20,7 +20,7 @@ namespace MQTTnet.Server
public MqttApplicationMessage ApplicationMessage { get; private set; } public MqttApplicationMessage ApplicationMessage { get; private set; }
public MqttApplicationMessageResponse Response { get; private set; } = new MqttApplicationMessageResponse(); public PublishResponse Response { get; private set; } = new PublishResponse();
public bool ProcessPublish { get; private set; } = true; public bool ProcessPublish { get; private set; } = true;
@@ -29,13 +29,13 @@ namespace MQTTnet.Server
public async Task Invoke(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken) public async Task Invoke(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken)
{ {
// Reset to defaults. // Reset to defaults.
Response = new MqttApplicationMessageResponse(); Response = new PublishResponse();
ProcessPublish = true; ProcessPublish = true;
CloseConnection = false; CloseConnection = false;
ApplicationMessage = applicationMessage; ApplicationMessage = applicationMessage;
// Intercept. // Intercept.
var eventArgs = new InterceptingMqttClientPublishEventArgs var eventArgs = new InterceptingPublishEventArgs
{ {
ClientId = _clientId, ClientId = _clientId,
ApplicationMessage = applicationMessage, ApplicationMessage = applicationMessage,
@@ -53,7 +53,7 @@ namespace MQTTnet.Server
eventArgs.ProcessPublish = false; eventArgs.ProcessPublish = false;
} }
await _eventContainer.InterceptingClientPublishEvent.InvokeAsync(eventArgs).ConfigureAwait(false); await _eventContainer.InterceptingPublishEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
// Expose results. // Expose results.
ProcessPublish = eventArgs.ProcessPublish && eventArgs.ApplicationMessage != null; ProcessPublish = eventArgs.ProcessPublish && eventArgs.ApplicationMessage != null;
@@ -14,7 +14,7 @@ using MQTTnet.Protocol;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
public sealed class MqttClientConnection : IDisposable public sealed class MqttClient : IDisposable
{ {
readonly Dictionary<ushort, string> _topicAlias = new Dictionary<ushort, string>(); readonly Dictionary<ushort, string> _topicAlias = new Dictionary<ushort, string>();
readonly MqttPacketDispatcher _packetDispatcher = new MqttPacketDispatcher(); readonly MqttPacketDispatcher _packetDispatcher = new MqttPacketDispatcher();
@@ -32,10 +32,10 @@ namespace MQTTnet.Server
CancellationTokenSource _cancellationToken; CancellationTokenSource _cancellationToken;
public MqttClientConnection( public MqttClient(
MqttConnectPacket connectPacket, MqttConnectPacket connectPacket,
IMqttChannelAdapter channelAdapter, IMqttChannelAdapter channelAdapter,
MqttClientSession session, MqttSession session,
MqttServerOptions serverOptions, MqttServerOptions serverOptions,
MqttServerEventContainer eventContainer, MqttServerEventContainer eventContainer,
MqttClientSessionsManager sessionsManager, MqttClientSessionsManager sessionsManager,
@@ -54,7 +54,7 @@ namespace MQTTnet.Server
_applicationMessageInterceptorInvoker = new MqttApplicationMessageInterceptorInvoker(eventContainer, Id, Session.Items); _applicationMessageInterceptorInvoker = new MqttApplicationMessageInterceptorInvoker(eventContainer, Id, Session.Items);
if (logger == null) throw new ArgumentNullException(nameof(logger)); if (logger == null) throw new ArgumentNullException(nameof(logger));
_logger = logger.WithSource(nameof(MqttClientConnection)); _logger = logger.WithSource(nameof(MqttClient));
} }
public string Id => _connectPacket.ClientId; public string Id => _connectPacket.ClientId;
@@ -63,13 +63,13 @@ namespace MQTTnet.Server
public IMqttChannelAdapter ChannelAdapter { get; } public IMqttChannelAdapter ChannelAdapter { get; }
public MqttClientConnectionStatistics Statistics { get; } = new MqttClientConnectionStatistics(); public MqttClientStatistics Statistics { get; } = new MqttClientStatistics();
public bool IsRunning { get; private set; } public bool IsRunning { get; private set; }
public ushort KeepAlivePeriod => _connectPacket.KeepAlivePeriod; public ushort KeepAlivePeriod => _connectPacket.KeepAlivePeriod;
public MqttClientSession Session { get; } public MqttSession Session { get; }
public bool IsTakenOver { get; set; } public bool IsTakenOver { get; set; }
@@ -20,14 +20,14 @@ namespace MQTTnet.Server
readonly MqttPacketFactories _packetFactories = new MqttPacketFactories(); readonly MqttPacketFactories _packetFactories = new MqttPacketFactories();
readonly AsyncLock _createConnectionSyncRoot = new AsyncLock(); readonly AsyncLock _createConnectionSyncRoot = new AsyncLock();
readonly Dictionary<string, MqttClientConnection> _clientConnections = new Dictionary<string, MqttClientConnection>(4096); readonly Dictionary<string, MqttClient> _clients = new Dictionary<string, MqttClient>(4096);
readonly Dictionary<string, MqttClientSession> _clientSessions = new Dictionary<string, MqttClientSession>(4096); readonly Dictionary<string, MqttSession> _sessions = new Dictionary<string, MqttSession>(4096);
readonly IDictionary<object, object> _serverSessionItems = new ConcurrentDictionary<object, object>(); readonly IDictionary<object, object> _serverSessionItems = new ConcurrentDictionary<object, object>();
readonly MqttConnAckPacketFactory _connAckPacketFactory = new MqttConnAckPacketFactory(); readonly MqttConnAckPacketFactory _connAckPacketFactory = new MqttConnAckPacketFactory();
readonly IMqttRetainedMessagesManager _retainedMessagesManager; readonly MqttRetainedMessagesManager _retainedMessagesManager;
readonly MqttServerEventContainer _eventContainer; readonly MqttServerEventContainer _eventContainer;
readonly MqttServerOptions _options; readonly MqttServerOptions _options;
readonly MqttNetSourceLogger _logger; readonly MqttNetSourceLogger _logger;
@@ -35,7 +35,7 @@ namespace MQTTnet.Server
public MqttClientSessionsManager( public MqttClientSessionsManager(
MqttServerOptions options, MqttServerOptions options,
IMqttRetainedMessagesManager retainedMessagesManager, MqttRetainedMessagesManager retainedMessagesManager,
MqttServerEventContainer eventContainer, MqttServerEventContainer eventContainer,
IMqttNetLogger logger) IMqttNetLogger logger)
{ {
@@ -78,7 +78,7 @@ namespace MQTTnet.Server
public async Task HandleClientConnectionAsync(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken) public async Task HandleClientConnectionAsync(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
{ {
MqttClientConnection clientConnection = null; MqttClient client = null;
try try
{ {
@@ -103,11 +103,11 @@ namespace MQTTnet.Server
connAckPacket = _connAckPacketFactory.Create(connectionValidatorContext); connAckPacket = _connAckPacketFactory.Create(connectionValidatorContext);
// Pass connAckPacket so that IsSessionPresent flag can be set if the client session already exists // Pass connAckPacket so that IsSessionPresent flag can be set if the client session already exists
clientConnection = await CreateClientConnection(connectPacket, connAckPacket, channelAdapter, connectionValidatorContext.SessionItems).ConfigureAwait(false); client = await CreateClientConnection(connectPacket, connAckPacket, channelAdapter, connectionValidatorContext.SessionItems).ConfigureAwait(false);
await clientConnection.SendPacketAsync(connAckPacket, cancellationToken).ConfigureAwait(false); await client.SendPacketAsync(connAckPacket, cancellationToken).ConfigureAwait(false);
await _eventContainer.ClientConnectedEvent.InvokeAsync(() => new MqttServerClientConnectedEventArgs await _eventContainer.ClientConnectedEvent.InvokeAsync(() => new ClientConnectedEventArgs
{ {
ClientId = connectPacket.ClientId, ClientId = connectPacket.ClientId,
UserName = connectPacket.Username, UserName = connectPacket.Username,
@@ -115,7 +115,7 @@ namespace MQTTnet.Server
Endpoint = channelAdapter.Endpoint Endpoint = channelAdapter.Endpoint
}).ConfigureAwait(false); }).ConfigureAwait(false);
await clientConnection.RunAsync().ConfigureAwait(false); await client.RunAsync().ConfigureAwait(false);
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@@ -126,33 +126,33 @@ namespace MQTTnet.Server
} }
finally finally
{ {
if (clientConnection != null) if (client != null)
{ {
if (clientConnection.Id != null) if (client.Id != null)
{ {
// in case it is a takeover _clientConnections already contains the new connection // in case it is a takeover _clientConnections already contains the new connection
if (!clientConnection.IsTakenOver) if (!client.IsTakenOver)
{ {
lock (_clientConnections) lock (_clients)
{ {
_clientConnections.Remove(clientConnection.Id); _clients.Remove(client.Id);
} }
if (!_options.EnablePersistentSessions) if (!_options.EnablePersistentSessions)
{ {
await DeleteSessionAsync(clientConnection.Id).ConfigureAwait(false); await DeleteSessionAsync(client.Id).ConfigureAwait(false);
} }
} }
} }
var endpoint = clientConnection.Endpoint; var endpoint = client.Endpoint;
if (clientConnection.Id != null && !clientConnection.IsTakenOver) if (client.Id != null && !client.IsTakenOver)
{ {
await _eventContainer.ClientDisconnectedEvent.InvokeAsync(() => new MqttServerClientDisconnectedEventArgs await _eventContainer.ClientDisconnectedEvent.InvokeAsync(() => new ClientDisconnectedEventArgs
{ {
ClientId = clientConnection.Id, ClientId = client.Id,
DisconnectType = clientConnection.IsCleanDisconnect ? MqttClientDisconnectType.Clean : MqttClientDisconnectType.NotClean, DisconnectType = client.IsCleanDisconnect ? MqttClientDisconnectType.Clean : MqttClientDisconnectType.NotClean,
Endpoint = endpoint Endpoint = endpoint
}).ConfigureAwait(false); }).ConfigureAwait(false);
} }
@@ -167,11 +167,11 @@ namespace MQTTnet.Server
public async Task CloseAllConnectionsAsync() public async Task CloseAllConnectionsAsync()
{ {
List<MqttClientConnection> connections; List<MqttClient> connections;
lock (_clientConnections) lock (_clients)
{ {
connections = _clientConnections.Values.ToList(); connections = _clients.Values.ToList();
_clientConnections.Clear(); _clients.Clear();
} }
foreach (var connection in connections) foreach (var connection in connections)
@@ -180,11 +180,11 @@ namespace MQTTnet.Server
} }
} }
public List<MqttClientConnection> GetConnections() public List<MqttClient> GetConnections()
{ {
lock (_clientConnections) lock (_clients)
{ {
return _clientConnections.Values.ToList(); return _clients.Values.ToList();
} }
} }
@@ -192,9 +192,9 @@ namespace MQTTnet.Server
{ {
var result = new List<IMqttClientStatus>(); var result = new List<IMqttClientStatus>();
lock (_clientConnections) lock (_clients)
{ {
foreach (var connection in _clientConnections.Values) foreach (var connection in _clients.Values)
{ {
var clientStatus = new MqttClientStatus(connection) var clientStatus = new MqttClientStatus(connection)
{ {
@@ -212,9 +212,9 @@ namespace MQTTnet.Server
{ {
var result = new List<IMqttSessionStatus>(); var result = new List<IMqttSessionStatus>();
lock (_clientSessions) lock (_sessions)
{ {
foreach (var session in _clientSessions.Values) foreach (var session in _sessions.Values)
{ {
var sessionStatus = new MqttSessionStatus(session); var sessionStatus = new MqttSessionStatus(session);
result.Add(sessionStatus); result.Add(sessionStatus);
@@ -263,18 +263,18 @@ namespace MQTTnet.Server
public async Task DeleteSessionAsync(string clientId) public async Task DeleteSessionAsync(string clientId)
{ {
MqttClientConnection connection; MqttClient connection;
MqttClientSession session; MqttSession session;
lock (_clientConnections) lock (_clients)
{ {
_clientConnections.TryGetValue(clientId, out connection); _clients.TryGetValue(clientId, out connection);
} }
lock (_clientSessions) lock (_sessions)
{ {
_clientSessions.TryGetValue(clientId, out session); _sessions.TryGetValue(clientId, out session);
_clientSessions.Remove(clientId); _sessions.Remove(clientId);
} }
try try
@@ -331,67 +331,16 @@ namespace MQTTnet.Server
{ {
try try
{ {
// MqttPendingApplicationMessage pendingApplicationMessage;
// try
// {
// pendingApplicationMessage = _messageQueue.Take();
// }
// catch (ArgumentNullException)
// {
// return;
// }
// // catch (ThreadAbortException)
// // {
// // return;
// // }
// catch (ObjectDisposedException)
// {
// return;
// }
//var clientConnection = pendingApplicationMessage.Sender;
//var senderClientId = clientConnection?.Id ?? _options.ClientId;
// // TODO: Only use PUBLISH packets internally!
// var applicationMessage = new MqttApplicationMessageFactory().Create(pendingApplicationMessage.PublishPacket);
//
// var interceptor = _options.ApplicationMessageInterceptor;
// if (interceptor != null)
// {
// var interceptorContext = await InterceptApplicationMessageAsync(interceptor, clientConnection, applicationMessage).ConfigureAwait(false);
// if (interceptorContext != null)
// {
// if (interceptorContext.CloseConnection)
// {
// if (clientConnection != null)
// {
// await clientConnection.StopAsync(MqttClientDisconnectReason.NormalDisconnection).ConfigureAwait(false);
// }
// }
//
// if (interceptorContext.ApplicationMessage == null || !interceptorContext.ProcessPublish)
// {
// return;
// }
//
// applicationMessage = interceptorContext.ApplicationMessage;
// }
// }
// await _eventContainer.ApplicationMessageReceivedEvent.InvokeAsync(
// () => new MqttApplicationMessageReceivedEventArgs(senderClientId, applicationMessage, null, null))
// .ConfigureAwait(false);
if (applicationMessage.Retain) if (applicationMessage.Retain)
{ {
await _retainedMessagesManager.HandleMessageAsync(senderClientId, applicationMessage).ConfigureAwait(false); await _retainedMessagesManager.UpdateMessage(senderClientId, applicationMessage).ConfigureAwait(false);
} }
var deliveryCount = 0; var deliveryCount = 0;
List<MqttClientSession> sessions; List<MqttSession> sessions;
lock (_clientSessions) lock (_sessions)
{ {
sessions = _clientSessions.Values.ToList(); sessions = _sessions.Values.ToList();
} }
foreach (var clientSession in sessions) foreach (var clientSession in sessions)
@@ -434,7 +383,7 @@ namespace MQTTnet.Server
if (deliveryCount == 0) if (deliveryCount == 0)
{ {
await _eventContainer.ApplicationMessageNotConsumedEvent.InvokeAsync(() => new MqttApplicationMessageNotConsumedEventArgs await _eventContainer.ApplicationMessageNotConsumedEvent.InvokeAsync(() => new ApplicationMessageNotConsumedEventArgs
{ {
ApplicationMessage = applicationMessage, ApplicationMessage = applicationMessage,
SenderClientId = senderClientId SenderClientId = senderClientId
@@ -447,14 +396,14 @@ namespace MQTTnet.Server
} }
} }
async Task<ValidatingMqttClientConnectionEventArgs> ValidateConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter) async Task<ValidatingConnectionEventArgs> ValidateConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter)
{ {
var context = new ValidatingMqttClientConnectionEventArgs(connectPacket, channelAdapter) var context = new ValidatingConnectionEventArgs(connectPacket, channelAdapter)
{ {
SessionItems = new ConcurrentDictionary<object, object>() SessionItems = new ConcurrentDictionary<object, object>()
}; };
await _eventContainer.ValidatingClientConnectionEvent.InvokeAsync(context).ConfigureAwait(false); await _eventContainer.ValidatingConnectionEvent.InvokeAsync(context).ConfigureAwait(false);
// Check the client ID and set a random one if supported. // Check the client ID and set a random one if supported.
if (string.IsNullOrEmpty(connectPacket.ClientId) && channelAdapter.PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.V500) if (string.IsNullOrEmpty(connectPacket.ClientId) && channelAdapter.PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.V500)
@@ -470,22 +419,21 @@ namespace MQTTnet.Server
return context; return context;
} }
async Task<MqttClientConnection> CreateClientConnection( async Task<MqttClient> CreateClientConnection(
MqttConnectPacket connectPacket, MqttConnectPacket connectPacket,
MqttConnAckPacket connAckPacket, MqttConnAckPacket connAckPacket,
IMqttChannelAdapter channelAdapter, IMqttChannelAdapter channelAdapter,
IDictionary<object, object> sessionItems) IDictionary<object, object> sessionItems)
{ {
MqttClientConnection connection; MqttClient connection;
using (await _createConnectionSyncRoot.WaitAsync(CancellationToken.None).ConfigureAwait(false)) using (await _createConnectionSyncRoot.WaitAsync(CancellationToken.None).ConfigureAwait(false))
{ {
MqttClientSession session; MqttSession session;
lock (_clientSessions) lock (_sessions)
{ {
if (!_clientSessions.TryGetValue(connectPacket.ClientId, out session)) if (!_sessions.TryGetValue(connectPacket.ClientId, out session))
{ {
_logger.Verbose("Created a new session for client '{0}'.", connectPacket.ClientId);
session = CreateSession(connectPacket.ClientId, sessionItems); session = CreateSession(connectPacket.ClientId, sessionItems);
} }
else else
@@ -503,29 +451,38 @@ namespace MQTTnet.Server
} }
} }
_clientSessions[connectPacket.ClientId] = session; _sessions[connectPacket.ClientId] = session;
} }
MqttClientConnection existingConnection; if (!connAckPacket.IsSessionPresent)
lock (_clientConnections)
{ {
_clientConnections.TryGetValue(connectPacket.ClientId, out existingConnection); var preparingSessionEventArgs = new PreparingSessionEventArgs();
await _eventContainer.PreparingSessionEvent.InvokeAsync(preparingSessionEventArgs).ConfigureAwait(false);
// TODO: Import subscriptions etc.
//session.SubscriptionsManager.Subscribe()
}
MqttClient existing;
lock (_clients)
{
_clients.TryGetValue(connectPacket.ClientId, out existing);
connection = CreateConnection(connectPacket, channelAdapter, session); connection = CreateConnection(connectPacket, channelAdapter, session);
_clientConnections[connectPacket.ClientId] = connection; _clients[connectPacket.ClientId] = connection;
} }
if (existingConnection != null) if (existing != null)
{ {
existingConnection.IsTakenOver = true; existing.IsTakenOver = true;
await existingConnection.StopAsync(MqttClientDisconnectReason.SessionTakenOver).ConfigureAwait(false); await existing.StopAsync(MqttClientDisconnectReason.SessionTakenOver).ConfigureAwait(false);
await _eventContainer.ClientDisconnectedEvent.InvokeAsync(() => new MqttServerClientDisconnectedEventArgs await _eventContainer.ClientDisconnectedEvent.InvokeAsync(() => new ClientDisconnectedEventArgs
{ {
ClientId = existingConnection.Id, ClientId = existing.Id,
DisconnectType = MqttClientDisconnectType.Takeover, DisconnectType = MqttClientDisconnectType.Takeover,
Endpoint = existingConnection.Endpoint Endpoint = existing.Endpoint
}).ConfigureAwait(false); }).ConfigureAwait(false);
} }
} }
@@ -566,11 +523,11 @@ namespace MQTTnet.Server
// return interceptorContext; // return interceptorContext;
// } // }
MqttClientSession GetClientSession(string clientId) MqttSession GetClientSession(string clientId)
{ {
lock (_clientSessions) lock (_sessions)
{ {
if (!_clientSessions.TryGetValue(clientId, out var session)) if (!_sessions.TryGetValue(clientId, out var session))
{ {
throw new InvalidOperationException($"Client session '{clientId}' is unknown."); throw new InvalidOperationException($"Client session '{clientId}' is unknown.");
} }
@@ -579,9 +536,9 @@ namespace MQTTnet.Server
} }
} }
MqttClientConnection CreateConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter, MqttClientSession session) MqttClient CreateConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter, MqttSession session)
{ {
return new MqttClientConnection( return new MqttClient(
connectPacket, connectPacket,
channelAdapter, channelAdapter,
session, session,
@@ -591,9 +548,11 @@ namespace MQTTnet.Server
_rootLogger); _rootLogger);
} }
MqttClientSession CreateSession(string clientId, IDictionary<object, object> sessionItems) MqttSession CreateSession(string clientId, IDictionary<object, object> sessionItems)
{ {
return new MqttClientSession( _logger.Verbose("Created a new session for client '{0}'.", clientId);
return new MqttSession(
clientId, clientId,
sessionItems, sessionItems,
_options, _options,
@@ -606,12 +565,12 @@ namespace MQTTnet.Server
{ {
_createConnectionSyncRoot?.Dispose(); _createConnectionSyncRoot?.Dispose();
foreach (var clientConnection in _clientConnections.Values) foreach (var clientConnection in _clients.Values)
{ {
clientConnection.Dispose(); clientConnection.Dispose();
} }
foreach (var session in _clientSessions.Values) foreach (var session in _sessions.Values)
{ {
session.Dispose(); session.Dispose();
} }
@@ -4,7 +4,7 @@ using MQTTnet.Packets;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
public sealed class MqttClientConnectionStatistics public sealed class MqttClientStatistics
{ {
// Start with 1 because the CONNACK packet is not counted here. // Start with 1 because the CONNACK packet is not counted here.
long _receivedPacketsCount = 1; long _receivedPacketsCount = 1;
@@ -15,7 +15,7 @@ namespace MQTTnet.Server
long _receivedApplicationMessagesCount; long _receivedApplicationMessagesCount;
long _sentApplicationMessagesCount; long _sentApplicationMessagesCount;
public MqttClientConnectionStatistics() public MqttClientStatistics()
{ {
ConnectedTimestamp = DateTime.UtcNow; ConnectedTimestamp = DateTime.UtcNow;
@@ -16,29 +16,29 @@ namespace MQTTnet.Server
readonly ReaderWriterLockSlim _subscriptionsLock = new ReaderWriterLockSlim(); readonly ReaderWriterLockSlim _subscriptionsLock = new ReaderWriterLockSlim();
readonly Dictionary<string, MqttSubscription> _subscriptions = new Dictionary<string, MqttSubscription>(4096); readonly Dictionary<string, MqttSubscription> _subscriptions = new Dictionary<string, MqttSubscription>(4096);
readonly MqttClientSession _clientSession; readonly MqttSession _session;
readonly MqttServerOptions _options; readonly MqttServerOptions _options;
readonly MqttServerEventContainer _eventContainer; readonly MqttServerEventContainer _eventContainer;
readonly IMqttRetainedMessagesManager _retainedMessagesManager; readonly MqttRetainedMessagesManager _retainedMessagesManager;
public MqttClientSubscriptionsManager( public MqttClientSubscriptionsManager(
MqttClientSession clientSession, MqttSession session,
MqttServerOptions serverOptions, MqttServerOptions serverOptions,
MqttServerEventContainer eventContainer, MqttServerEventContainer eventContainer,
IMqttRetainedMessagesManager retainedMessagesManager) MqttRetainedMessagesManager retainedMessagesManager)
{ {
_clientSession = clientSession ?? throw new ArgumentNullException(nameof(clientSession)); _session = session ?? throw new ArgumentNullException(nameof(session));
_options = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions)); _options = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions));
_eventContainer = eventContainer ?? throw new ArgumentNullException(nameof(eventContainer)); _eventContainer = eventContainer ?? throw new ArgumentNullException(nameof(eventContainer));
_retainedMessagesManager = retainedMessagesManager ?? throw new ArgumentNullException(nameof(retainedMessagesManager)); _retainedMessagesManager = retainedMessagesManager ?? throw new ArgumentNullException(nameof(retainedMessagesManager));
} }
public async Task<MqttSubscribeResult> Subscribe(MqttSubscribePacket subscribePacket, CancellationToken cancellationToken) public async Task<SubscribeResult> Subscribe(MqttSubscribePacket subscribePacket, CancellationToken cancellationToken)
{ {
if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket)); if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
var retainedApplicationMessages = await _retainedMessagesManager.GetMessagesAsync().ConfigureAwait(false); var retainedApplicationMessages = await _retainedMessagesManager.GetMessages().ConfigureAwait(false);
var result = new MqttSubscribeResult(); var result = new SubscribeResult();
// The topic filters are order by its QoS so that the higher QoS will win over a // The topic filters are order by its QoS so that the higher QoS will win over a
// lower one. // lower one.
@@ -79,9 +79,9 @@ namespace MQTTnet.Server
subscribePacket.Properties?.SubscriptionIdentifier ?? 0, subscribePacket.Properties?.SubscriptionIdentifier ?? 0,
interceptorContext.Response.ReasonCode); interceptorContext.Response.ReasonCode);
await _eventContainer.ClientSubscribedTopicEvent.InvokeAsync(() => new MqttServerClientSubscribedTopicEventArgs await _eventContainer.ClientSubscribedTopicEvent.InvokeAsync(() => new ClientSubscribedTopicEventArgs
{ {
ClientId = _clientSession.Id, ClientId = _session.Id,
TopicFilter = finalTopicFilter TopicFilter = finalTopicFilter
}).ConfigureAwait(false); }).ConfigureAwait(false);
@@ -135,9 +135,9 @@ namespace MQTTnet.Server
foreach (var topicFilter in unsubscribePacket.TopicFilters) foreach (var topicFilter in unsubscribePacket.TopicFilters)
{ {
await _eventContainer.ClientUnsubscribedTopicEvent.InvokeAsync(() => new MqttServerClientUnsubscribedTopicEventArgs await _eventContainer.ClientUnsubscribedTopicEvent.InvokeAsync(() => new ClientUnsubscribedTopicEventArgs
{ {
ClientId = _clientSession.Id, ClientId = _session.Id,
TopicFilter = topicFilter TopicFilter = topicFilter
}).ConfigureAwait(false); }).ConfigureAwait(false);
} }
@@ -159,7 +159,7 @@ namespace MQTTnet.Server
_subscriptionsLock.ExitReadLock(); _subscriptionsLock.ExitReadLock();
} }
var senderIsReceiver = string.Equals(senderClientId, _clientSession.Id); var senderIsReceiver = string.Equals(senderClientId, _session.Id);
var qosLevels = new HashSet<MqttQualityOfServiceLevel>(); var qosLevels = new HashSet<MqttQualityOfServiceLevel>();
var subscriptionIdentifiers = new HashSet<uint>(); var subscriptionIdentifiers = new HashSet<uint>();
@@ -262,7 +262,7 @@ namespace MQTTnet.Server
} }
static void FilterRetainedApplicationMessages(IList<MqttApplicationMessage> retainedApplicationMessages, CreateSubscriptionResult createSubscriptionResult, static void FilterRetainedApplicationMessages(IList<MqttApplicationMessage> retainedApplicationMessages, CreateSubscriptionResult createSubscriptionResult,
MqttSubscribeResult subscribeResult) SubscribeResult subscribeResult)
{ {
for (var i = retainedApplicationMessages.Count - 1; i >= 0; i--) for (var i = retainedApplicationMessages.Count - 1; i >= 0; i--)
{ {
@@ -294,14 +294,13 @@ namespace MQTTnet.Server
var queuedApplicationMessage = new MqttQueuedApplicationMessage var queuedApplicationMessage = new MqttQueuedApplicationMessage
{ {
ApplicationMessage = retainedApplicationMessage, ApplicationMessage = retainedApplicationMessage,
IsRetainedMessage = true,
SubscriptionQualityOfServiceLevel = createSubscriptionResult.Subscription.GrantedQualityOfServiceLevel SubscriptionQualityOfServiceLevel = createSubscriptionResult.Subscription.GrantedQualityOfServiceLevel
}; };
if (createSubscriptionResult.Subscription.Identifier > 0) // if (createSubscriptionResult.Subscription.Identifier > 0)
{ // {
queuedApplicationMessage.SubscriptionIdentifiers = new List<uint> {createSubscriptionResult.Subscription.Identifier}; // queuedApplicationMessage.SubscriptionIdentifiers = new List<uint> {createSubscriptionResult.Subscription.Identifier};
} // }
subscribeResult.RetainedApplicationMessages.Add(queuedApplicationMessage); subscribeResult.RetainedApplicationMessages.Add(queuedApplicationMessage);
@@ -309,14 +308,14 @@ namespace MQTTnet.Server
} }
} }
async Task<InterceptingMqttClientSubscriptionEventArgs> InterceptSubscribe(MqttTopicFilter topicFilter, CancellationToken cancellationToken) async Task<InterceptingSubscriptionEventArgs> InterceptSubscribe(MqttTopicFilter topicFilter, CancellationToken cancellationToken)
{ {
var subscriptionReceivedEventArgs = new InterceptingMqttClientSubscriptionEventArgs var subscriptionReceivedEventArgs = new InterceptingSubscriptionEventArgs
{ {
ClientId = _clientSession.Id, ClientId = _session.Id,
TopicFilter = topicFilter, TopicFilter = topicFilter,
SessionItems = _clientSession.Items, SessionItems = _session.Items,
Session = new MqttSessionStatus(_clientSession), Session = new MqttSessionStatus(_session),
CancellationToken = cancellationToken CancellationToken = cancellationToken
}; };
@@ -339,19 +338,19 @@ namespace MQTTnet.Server
} }
else else
{ {
await _eventContainer.InterceptingClientSubscriptionEvent.InvokeAsync(subscriptionReceivedEventArgs).ConfigureAwait(false); await _eventContainer.InterceptingSubscriptionEvent.InvokeAsync(subscriptionReceivedEventArgs).ConfigureAwait(false);
} }
return subscriptionReceivedEventArgs; return subscriptionReceivedEventArgs;
} }
async Task<InterceptingMqttClientUnsubscriptionEventArgs> InterceptUnsubscribe(string topicFilter, MqttSubscription mqttSubscription, CancellationToken cancellationToken) async Task<InterceptingUnsubscriptionEventArgs> InterceptUnsubscribe(string topicFilter, MqttSubscription mqttSubscription, CancellationToken cancellationToken)
{ {
var clientUnsubscribingTopicEventArgs = new InterceptingMqttClientUnsubscriptionEventArgs var clientUnsubscribingTopicEventArgs = new InterceptingUnsubscriptionEventArgs
{ {
ClientId = _clientSession.Id, ClientId = _session.Id,
Topic = topicFilter, Topic = topicFilter,
SessionItems = _clientSession.Items, SessionItems = _session.Items,
CancellationToken = cancellationToken CancellationToken = cancellationToken
}; };
@@ -364,7 +363,7 @@ namespace MQTTnet.Server
clientUnsubscribingTopicEventArgs.Response.ReasonCode = MqttUnsubscribeReasonCode.Success; clientUnsubscribingTopicEventArgs.Response.ReasonCode = MqttUnsubscribeReasonCode.Success;
} }
await _eventContainer.InterceptingClientUnsubscriptionEvent.InvokeAsync(clientUnsubscribingTopicEventArgs).ConfigureAwait(false); await _eventContainer.InterceptingUnsubscriptionEvent.InvokeAsync(clientUnsubscribingTopicEventArgs).ConfigureAwait(false);
return clientUnsubscribingTopicEventArgs; return clientUnsubscribingTopicEventArgs;
} }
@@ -9,45 +9,36 @@ using MQTTnet.Internal;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
public sealed class MqttRetainedMessagesManager : IMqttRetainedMessagesManager public sealed class MqttRetainedMessagesManager
{ {
readonly AsyncLock _storageAccessLock = new AsyncLock();
readonly Dictionary<string, MqttApplicationMessage> _messages = new Dictionary<string, MqttApplicationMessage>(4096); readonly Dictionary<string, MqttApplicationMessage> _messages = new Dictionary<string, MqttApplicationMessage>(4096);
readonly AsyncLock _storageAccessLock = new AsyncLock();
MqttNetSourceLogger _logger; readonly MqttServerEventContainer _eventContainer;
MqttServerOptions _options; readonly MqttNetSourceLogger _logger;
// TODO: Get rid of the logger here! public MqttRetainedMessagesManager(MqttServerEventContainer eventContainer, IMqttNetLogger logger)
public Task Start(MqttServerOptions options, IMqttNetLogger logger)
{ {
_eventContainer = eventContainer ?? throw new ArgumentNullException(nameof(eventContainer));
if (logger == null) throw new ArgumentNullException(nameof(logger)); if (logger == null) throw new ArgumentNullException(nameof(logger));
_logger = logger.WithSource(nameof(MqttRetainedMessagesManager)); _logger = logger.WithSource(nameof(MqttRetainedMessagesManager));
_options = options ?? throw new ArgumentNullException(nameof(options));
return PlatformAbstractionLayer.CompletedTask;
} }
public async Task LoadMessagesAsync() public async Task LoadMessages()
{ {
if (_options.Storage == null)
{
return;
}
try try
{ {
var retainedMessages = await _options.Storage.LoadRetainedMessagesAsync().ConfigureAwait(false); var eventArgs = new LoadingRetainedMessagesEventArgs();
await _eventContainer.LoadingRetainedMessagesEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
lock (_messages) lock (_messages)
{ {
_messages.Clear(); _messages.Clear();
if (retainedMessages != null) foreach (var retainedMessage in eventArgs.StoredRetainedMessages ?? new List<MqttApplicationMessage>())
{ {
foreach (var retainedMessage in retainedMessages) _messages[retainedMessage.Topic] = retainedMessage;
{
_messages[retainedMessage.Topic] = retainedMessage;
}
} }
} }
} }
@@ -57,7 +48,7 @@ namespace MQTTnet.Server
} }
} }
public async Task HandleMessageAsync(string clientId, MqttApplicationMessage applicationMessage) public async Task UpdateMessage(string clientId, MqttApplicationMessage applicationMessage)
{ {
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage)); if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
@@ -102,12 +93,16 @@ namespace MQTTnet.Server
if (saveIsRequired) if (saveIsRequired)
{ {
if (_options.Storage != null) using (await _storageAccessLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
{ {
using (await _storageAccessLock.WaitAsync(CancellationToken.None).ConfigureAwait(false)) var eventArgs = new RetainedMessageChangedEventArgs
{ {
await _options.Storage.SaveRetainedMessagesAsync(messagesForSave).ConfigureAwait(false); ClientId = clientId,
} ChangedRetainedMessage = applicationMessage,
StoredRetainedMessages = messagesForSave
};
await _eventContainer.RetainedMessageChangedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
} }
} }
} }
@@ -117,7 +112,7 @@ namespace MQTTnet.Server
} }
} }
public Task<IList<MqttApplicationMessage>> GetMessagesAsync() public Task<IList<MqttApplicationMessage>> GetMessages()
{ {
lock (_messages) lock (_messages)
{ {
@@ -125,19 +120,16 @@ namespace MQTTnet.Server
} }
} }
public async Task ClearMessagesAsync() public async Task ClearMessages()
{ {
lock (_messages) lock (_messages)
{ {
_messages.Clear(); _messages.Clear();
} }
if (_options.Storage != null) using (await _storageAccessLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
{ {
using (await _storageAccessLock.WaitAsync(CancellationToken.None).ConfigureAwait(false)) await _eventContainer.RetainedMessagesClearedEvent.InvokeAsync(EventArgs.Empty).ConfigureAwait(false);
{
await _options.Storage.SaveRetainedMessagesAsync(new List<MqttApplicationMessage>()).ConfigureAwait(false);
}
} }
} }
} }
@@ -5,29 +5,31 @@ namespace MQTTnet.Server
{ {
public sealed class MqttServerEventContainer public sealed class MqttServerEventContainer
{ {
public AsyncEvent<InterceptingMqttClientSubscriptionEventArgs> InterceptingClientSubscriptionEvent { get; } = new AsyncEvent<InterceptingMqttClientSubscriptionEventArgs>(); public AsyncEvent<InterceptingSubscriptionEventArgs> InterceptingSubscriptionEvent { get; } = new AsyncEvent<InterceptingSubscriptionEventArgs>();
public AsyncEvent<InterceptingMqttClientUnsubscriptionEventArgs> InterceptingClientUnsubscriptionEvent { get; } = new AsyncEvent<InterceptingMqttClientUnsubscriptionEventArgs>(); public AsyncEvent<InterceptingUnsubscriptionEventArgs> InterceptingUnsubscriptionEvent { get; } = new AsyncEvent<InterceptingUnsubscriptionEventArgs>();
public AsyncEvent<InterceptingMqttClientPublishEventArgs> InterceptingClientPublishEvent { get; } = new AsyncEvent<InterceptingMqttClientPublishEventArgs>(); public AsyncEvent<InterceptingPublishEventArgs> InterceptingPublishEvent { get; } = new AsyncEvent<InterceptingPublishEventArgs>();
public AsyncEvent<ValidatingMqttClientConnectionEventArgs> ValidatingClientConnectionEvent { get; } = new AsyncEvent<ValidatingMqttClientConnectionEventArgs>(); public AsyncEvent<ValidatingConnectionEventArgs> ValidatingConnectionEvent { get; } = new AsyncEvent<ValidatingConnectionEventArgs>();
public AsyncEvent<MqttServerClientConnectedEventArgs> ClientConnectedEvent { get; } = new AsyncEvent<MqttServerClientConnectedEventArgs>(); public AsyncEvent<ClientConnectedEventArgs> ClientConnectedEvent { get; } = new AsyncEvent<ClientConnectedEventArgs>();
public AsyncEvent<MqttServerClientDisconnectedEventArgs> ClientDisconnectedEvent { get; } = new AsyncEvent<MqttServerClientDisconnectedEventArgs>(); public AsyncEvent<ClientDisconnectedEventArgs> ClientDisconnectedEvent { get; } = new AsyncEvent<ClientDisconnectedEventArgs>();
public AsyncEvent<MqttServerClientSubscribedTopicEventArgs> ClientSubscribedTopicEvent { get; } = new AsyncEvent<MqttServerClientSubscribedTopicEventArgs>(); public AsyncEvent<ClientSubscribedTopicEventArgs> ClientSubscribedTopicEvent { get; } = new AsyncEvent<ClientSubscribedTopicEventArgs>();
public AsyncEvent<MqttServerClientUnsubscribedTopicEventArgs> ClientUnsubscribedTopicEvent { get; } = new AsyncEvent<MqttServerClientUnsubscribedTopicEventArgs>(); public AsyncEvent<ClientUnsubscribedTopicEventArgs> ClientUnsubscribedTopicEvent { get; } = new AsyncEvent<ClientUnsubscribedTopicEventArgs>();
public AsyncEvent<PreparingMqttClientSessionEventArgs> PreparingClientSessionEvent { get; } = new AsyncEvent<PreparingMqttClientSessionEventArgs>(); public AsyncEvent<PreparingSessionEventArgs> PreparingSessionEvent { get; } = new AsyncEvent<PreparingSessionEventArgs>();
public AsyncEvent<MqttApplicationMessageNotConsumedEventArgs> ApplicationMessageNotConsumedEvent { get; } = new AsyncEvent<MqttApplicationMessageNotConsumedEventArgs>(); public AsyncEvent<ApplicationMessageNotConsumedEventArgs> ApplicationMessageNotConsumedEvent { get; } = new AsyncEvent<ApplicationMessageNotConsumedEventArgs>();
public AsyncEvent<EventArgs> RetainedApplicationMessageChangedEvent { get; } = new AsyncEvent<EventArgs>(); public AsyncEvent<RetainedMessageChangedEventArgs> RetainedMessageChangedEvent { get; } = new AsyncEvent<RetainedMessageChangedEventArgs>();
public AsyncEvent<EventArgs> RetainedApplicationMessageClearedEvent { get; } = new AsyncEvent<EventArgs>(); public AsyncEvent<LoadingRetainedMessagesEventArgs> LoadingRetainedMessagesEvent { get; } = new AsyncEvent<LoadingRetainedMessagesEventArgs>();
public AsyncEvent<EventArgs> RetainedMessagesClearedEvent { get; } = new AsyncEvent<EventArgs>();
public AsyncEvent<InterceptingPacketEventArgs> InterceptingInboundPacketEvent { get; } = new AsyncEvent<InterceptingPacketEventArgs>(); public AsyncEvent<InterceptingPacketEventArgs> InterceptingInboundPacketEvent { get; } = new AsyncEvent<InterceptingPacketEventArgs>();
@@ -66,7 +66,7 @@ namespace MQTTnet.Server
} }
} }
void TryMaintainConnection(MqttClientConnection connection, DateTime now) void TryMaintainConnection(MqttClient connection, DateTime now)
{ {
try try
{ {
@@ -10,7 +10,7 @@ using MQTTnet.Protocol;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
public sealed class MqttClientSession : IDisposable public sealed class MqttSession : IDisposable
{ {
readonly MqttPacketBus _packetBus = new MqttPacketBus(); readonly MqttPacketBus _packetBus = new MqttPacketBus();
@@ -19,11 +19,11 @@ namespace MQTTnet.Server
readonly MqttServerOptions _serverOptions; readonly MqttServerOptions _serverOptions;
readonly MqttClientSessionsManager _clientSessionsManager; readonly MqttClientSessionsManager _clientSessionsManager;
public MqttClientSession(string clientId, public MqttSession(string clientId,
IDictionary<object, object> items, IDictionary<object, object> items,
MqttServerOptions serverOptions, MqttServerOptions serverOptions,
MqttServerEventContainer eventContainer, MqttServerEventContainer eventContainer,
IMqttRetainedMessagesManager retainedMessagesManager, MqttRetainedMessagesManager retainedMessagesManager,
MqttClientSessionsManager clientSessionsManager) MqttClientSessionsManager clientSessionsManager)
{ {
_serverOptions = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions)); _serverOptions = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions));
@@ -3,7 +3,7 @@ using MQTTnet.Protocol;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
public sealed class MqttSubscribeResult public sealed class SubscribeResult
{ {
public List<MqttSubscribeReturnCode> ReturnCodes { get; } = new List<MqttSubscribeReturnCode>(128); public List<MqttSubscribeReturnCode> ReturnCodes { get; } = new List<MqttSubscribeReturnCode>(128);
@@ -6,14 +6,6 @@ namespace MQTTnet.Server
public sealed class MqttQueuedApplicationMessage public sealed class MqttQueuedApplicationMessage
{ {
public MqttApplicationMessage ApplicationMessage { get; set; } public MqttApplicationMessage ApplicationMessage { get; set; }
public string SenderClientId { get; set; }
public bool IsRetainedMessage { get; set; }
public List<uint> SubscriptionIdentifiers { get; set; }
public bool IsDuplicate { get; set; }
/// <summary> /// <summary>
/// Gets or sets the subscription quality of service level. /// Gets or sets the subscription quality of service level.
+65 -38
View File
@@ -1,5 +1,4 @@
using MQTTnet.Adapter; using MQTTnet.Adapter;
using MQTTnet.Exceptions;
using MQTTnet.Protocol; using MQTTnet.Protocol;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -24,7 +23,7 @@ namespace MQTTnet.Server
readonly MqttNetSourceLogger _logger; readonly MqttNetSourceLogger _logger;
MqttClientSessionsManager _clientSessionsManager; MqttClientSessionsManager _clientSessionsManager;
IMqttRetainedMessagesManager _retainedMessagesManager; MqttRetainedMessagesManager _retainedMessagesManager;
MqttServerKeepAliveMonitor _keepAliveMonitor; MqttServerKeepAliveMonitor _keepAliveMonitor;
CancellationTokenSource _cancellationTokenSource; CancellationTokenSource _cancellationTokenSource;
@@ -52,19 +51,19 @@ namespace MQTTnet.Server
remove => _eventContainer.StoppedEvent.RemoveHandler(value); remove => _eventContainer.StoppedEvent.RemoveHandler(value);
} }
public event Func<ValidatingMqttClientConnectionEventArgs, Task> ValidatingClientConnectionAsync public event Func<ValidatingConnectionEventArgs, Task> ValidatingConnectionAsync
{ {
add => _eventContainer.ValidatingClientConnectionEvent.AddHandler(value); add => _eventContainer.ValidatingConnectionEvent.AddHandler(value);
remove => _eventContainer.ValidatingClientConnectionEvent.RemoveHandler(value); remove => _eventContainer.ValidatingConnectionEvent.RemoveHandler(value);
} }
public event Func<MqttServerClientConnectedEventArgs, Task> ClientConnectedAsync public event Func<ClientConnectedEventArgs, Task> ClientConnectedAsync
{ {
add => _eventContainer.ClientConnectedEvent.AddHandler(value); add => _eventContainer.ClientConnectedEvent.AddHandler(value);
remove => _eventContainer.ClientConnectedEvent.RemoveHandler(value); remove => _eventContainer.ClientConnectedEvent.RemoveHandler(value);
} }
public event Func<MqttServerClientDisconnectedEventArgs, Task> ClientDisconnectedAsync public event Func<ClientDisconnectedEventArgs, Task> ClientDisconnectedAsync
{ {
add => _eventContainer.ClientDisconnectedEvent.AddHandler(value); add => _eventContainer.ClientDisconnectedEvent.AddHandler(value);
remove => _eventContainer.ClientDisconnectedEvent.RemoveHandler(value); remove => _eventContainer.ClientDisconnectedEvent.RemoveHandler(value);
@@ -82,63 +81,69 @@ namespace MQTTnet.Server
remove => _eventContainer.InterceptingOutboundPacketEvent.RemoveHandler(value); remove => _eventContainer.InterceptingOutboundPacketEvent.RemoveHandler(value);
} }
public event Func<InterceptingMqttClientSubscriptionEventArgs, Task> InterceptingClientSubscriptionAsync public event Func<InterceptingSubscriptionEventArgs, Task> InterceptingSubscriptionAsync
{ {
add => _eventContainer.InterceptingClientSubscriptionEvent.AddHandler(value); add => _eventContainer.InterceptingSubscriptionEvent.AddHandler(value);
remove => _eventContainer.InterceptingClientSubscriptionEvent.RemoveHandler(value); remove => _eventContainer.InterceptingSubscriptionEvent.RemoveHandler(value);
} }
public event Func<InterceptingMqttClientUnsubscriptionEventArgs, Task> InterceptingClientUnsubscriptionAsync public event Func<InterceptingUnsubscriptionEventArgs, Task> InterceptingUnsubscriptionAsync
{ {
add => _eventContainer.InterceptingClientUnsubscriptionEvent.AddHandler(value); add => _eventContainer.InterceptingUnsubscriptionEvent.AddHandler(value);
remove => _eventContainer.InterceptingClientUnsubscriptionEvent.RemoveHandler(value); remove => _eventContainer.InterceptingUnsubscriptionEvent.RemoveHandler(value);
} }
public event Func<InterceptingMqttClientPublishEventArgs, Task> InterceptingClientPublishAsync public event Func<InterceptingPublishEventArgs, Task> InterceptingPublishAsync
{ {
add => _eventContainer.InterceptingClientPublishEvent.AddHandler(value); add => _eventContainer.InterceptingPublishEvent.AddHandler(value);
remove => _eventContainer.InterceptingClientPublishEvent.RemoveHandler(value); remove => _eventContainer.InterceptingPublishEvent.RemoveHandler(value);
} }
public event Func<MqttServerClientSubscribedTopicEventArgs, Task> ClientSubscribedTopicAsync public event Func<ClientSubscribedTopicEventArgs, Task> ClientSubscribedTopicAsync
{ {
add => _eventContainer.ClientSubscribedTopicEvent.AddHandler(value); add => _eventContainer.ClientSubscribedTopicEvent.AddHandler(value);
remove => _eventContainer.ClientSubscribedTopicEvent.RemoveHandler(value); remove => _eventContainer.ClientSubscribedTopicEvent.RemoveHandler(value);
} }
public event Func<MqttServerClientUnsubscribedTopicEventArgs, Task> ClientUnsubscribedTopicAsync public event Func<ClientUnsubscribedTopicEventArgs, Task> ClientUnsubscribedTopicAsync
{ {
add => _eventContainer.ClientUnsubscribedTopicEvent.AddHandler(value); add => _eventContainer.ClientUnsubscribedTopicEvent.AddHandler(value);
remove => _eventContainer.ClientUnsubscribedTopicEvent.RemoveHandler(value); remove => _eventContainer.ClientUnsubscribedTopicEvent.RemoveHandler(value);
} }
public event Func<PreparingMqttClientSessionEventArgs, Task> PreparingClientSessionAsync public event Func<PreparingSessionEventArgs, Task> PreparingSessionAsync
{ {
add => _eventContainer.PreparingClientSessionEvent.AddHandler(value); add => _eventContainer.PreparingSessionEvent.AddHandler(value);
remove => _eventContainer.PreparingClientSessionEvent.RemoveHandler(value); remove => _eventContainer.PreparingSessionEvent.RemoveHandler(value);
} }
public event Func<MqttApplicationMessageNotConsumedEventArgs, Task> ApplicationMessageNotConsumedAsync public event Func<ApplicationMessageNotConsumedEventArgs, Task> ApplicationMessageNotConsumedAsync
{ {
add => _eventContainer.ApplicationMessageNotConsumedEvent.AddHandler(value); add => _eventContainer.ApplicationMessageNotConsumedEvent.AddHandler(value);
remove => _eventContainer.ApplicationMessageNotConsumedEvent.RemoveHandler(value); remove => _eventContainer.ApplicationMessageNotConsumedEvent.RemoveHandler(value);
} }
public event Func<EventArgs, Task> RetainedApplicationMessageChangedAsync public event Func<RetainedMessageChangedEventArgs, Task> RetainedMessageChangedAsync
{ {
add => _eventContainer.RetainedApplicationMessageChangedEvent.AddHandler(value); add => _eventContainer.RetainedMessageChangedEvent.AddHandler(value);
remove => _eventContainer.RetainedApplicationMessageChangedEvent.RemoveHandler(value); remove => _eventContainer.RetainedMessageChangedEvent.RemoveHandler(value);
} }
public event Func<EventArgs, Task> RetainedApplicationMessagesClearedAsync public event Func<LoadingRetainedMessagesEventArgs, Task> LoadingRetainedMessageAsync
{ {
add => _eventContainer.RetainedApplicationMessageClearedEvent.AddHandler(value); add => _eventContainer.LoadingRetainedMessagesEvent.AddHandler(value);
remove => _eventContainer.RetainedApplicationMessageClearedEvent.RemoveHandler(value); remove => _eventContainer.LoadingRetainedMessagesEvent.RemoveHandler(value);
}
public event Func<EventArgs, Task> RetainedMessagesClearedAsync
{
add => _eventContainer.RetainedMessagesClearedEvent.AddHandler(value);
remove => _eventContainer.RetainedMessagesClearedEvent.RemoveHandler(value);
} }
public bool IsStarted => _cancellationTokenSource != null; public bool IsStarted => _cancellationTokenSource != null;
public Task<IList<IMqttClientStatus>> GetClientStatusAsync() public Task<IList<IMqttClientStatus>> GetClientsAsync()
{ {
ThrowIfDisposed(); ThrowIfDisposed();
ThrowIfNotStarted(); ThrowIfNotStarted();
@@ -146,7 +151,7 @@ namespace MQTTnet.Server
return _clientSessionsManager.GetClientStatusAsync(); return _clientSessionsManager.GetClientStatusAsync();
} }
public Task<IList<IMqttSessionStatus>> GetSessionStatusAsync() public Task<IList<IMqttSessionStatus>> GetSessionsAsync()
{ {
ThrowIfDisposed(); ThrowIfDisposed();
ThrowIfNotStarted(); ThrowIfNotStarted();
@@ -154,20 +159,44 @@ namespace MQTTnet.Server
return _clientSessionsManager.GetSessionStatusAsync(); return _clientSessionsManager.GetSessionStatusAsync();
} }
public Task<IList<MqttApplicationMessage>> GetRetainedApplicationMessagesAsync() /// <summary>
/// Gets all currently cached retained messages.
/// </summary>
/// <returns>A list of retained messages.</returns>
public Task<IList<MqttApplicationMessage>> GetRetainedMessagesAsync()
{ {
ThrowIfDisposed(); ThrowIfDisposed();
ThrowIfNotStarted(); ThrowIfNotStarted();
return _retainedMessagesManager.GetMessagesAsync(); return _retainedMessagesManager.GetMessages();
} }
public Task ClearRetainedApplicationMessagesAsync() /// <summary>
/// Deletes all retained messages.
/// </summary>
/// <returns>A task.</returns>
public Task DeleteRetainedMessagesAsync()
{ {
ThrowIfDisposed(); ThrowIfDisposed();
ThrowIfNotStarted(); ThrowIfNotStarted();
return _retainedMessagesManager?.ClearMessagesAsync() ?? PlatformAbstractionLayer.CompletedTask; 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) public Task SubscribeAsync(string clientId, ICollection<MqttTopicFilter> topicFilters)
@@ -220,10 +249,8 @@ namespace MQTTnet.Server
_cancellationTokenSource = new CancellationTokenSource(); _cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = _cancellationTokenSource.Token; var cancellationToken = _cancellationTokenSource.Token;
_retainedMessagesManager = _options.RetainedMessagesManager ?? throw new MqttConfigurationException("options.RetainedMessagesManager should not be null."); _retainedMessagesManager = new MqttRetainedMessagesManager(_eventContainer, _rootLogger);
await _retainedMessagesManager.LoadMessages().ConfigureAwait(false);
await _retainedMessagesManager.Start(_options, _rootLogger).ConfigureAwait(false);
await _retainedMessagesManager.LoadMessagesAsync().ConfigureAwait(false);
_clientSessionsManager = new MqttClientSessionsManager(_options, _retainedMessagesManager, _eventContainer, _rootLogger); _clientSessionsManager = new MqttClientSessionsManager(_options, _retainedMessagesManager, _eventContainer, _rootLogger);
+1 -107
View File
@@ -16,16 +16,7 @@ namespace MQTTnet.Server
return server.SubscribeAsync(clientId, topicFilters); return server.SubscribeAsync(clientId, topicFilters);
} }
public static Task SubscribeAsync(this MqttServer server, string clientId, string topic, MqttQualityOfServiceLevel qualityOfServiceLevel)
{
if (server == null) throw new ArgumentNullException(nameof(server));
if (clientId == null) throw new ArgumentNullException(nameof(clientId));
if (topic == null) throw new ArgumentNullException(nameof(topic));
return server.SubscribeAsync(clientId, new MqttTopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build());
}
public static Task SubscribeAsync(this MqttServer server, string clientId, string topic) public static Task SubscribeAsync(this MqttServer server, string clientId, string topic)
{ {
if (server == null) throw new ArgumentNullException(nameof(server)); if (server == null) throw new ArgumentNullException(nameof(server));
@@ -34,102 +25,5 @@ namespace MQTTnet.Server
return server.SubscribeAsync(clientId, new MqttTopicFilterBuilder().WithTopic(topic).Build()); return server.SubscribeAsync(clientId, new MqttTopicFilterBuilder().WithTopic(topic).Build());
} }
public static Task UnsubscribeAsync(this MqttServer server, string clientId, params string[] topicFilters)
{
if (server == null) throw new ArgumentNullException(nameof(server));
if (clientId == null) throw new ArgumentNullException(nameof(clientId));
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
return server.UnsubscribeAsync(clientId, topicFilters);
}
// public static async Task PublishAsync(this IMqttServer server, IEnumerable<MqttApplicationMessage> applicationMessages)
// {
// if (server == null) throw new ArgumentNullException(nameof(server));
// if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
//
// foreach (var applicationMessage in applicationMessages)
// {
// await server.PublishAsync(applicationMessage).ConfigureAwait(false);
// }
// }
// public static Task<MqttClientPublishResult> PublishAsync(this IMqttServer server, MqttApplicationMessage applicationMessage)
// {
// if (server == null) throw new ArgumentNullException(nameof(server));
// if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
//
// return server.PublishAsync(applicationMessage, CancellationToken.None);
// }
//
// public static async Task PublishAsync(this IMqttServer server, params MqttApplicationMessage[] applicationMessages)
// {
// if (server == null) throw new ArgumentNullException(nameof(server));
// if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
//
// foreach (var applicationMessage in applicationMessages)
// {
// await server.PublishAsync(applicationMessage, CancellationToken.None).ConfigureAwait(false);
// }
// }
// public static Task<MqttClientPublishResult> PublishAsync(this IMqttServer server, string topic)
// {
// if (server == null) throw new ArgumentNullException(nameof(server));
// if (topic == null) throw new ArgumentNullException(nameof(topic));
//
// return server.PublishAsync(builder => builder
// .WithTopic(topic));
// }
// public static Task<MqttClientPublishResult> PublishAsync(this IMqttServer server, string topic, string payload)
// {
// if (server == null) throw new ArgumentNullException(nameof(server));
// if (topic == null) throw new ArgumentNullException(nameof(topic));
//
// return server.PublishAsync(builder => builder
// .WithTopic(topic)
// .WithPayload(payload));
// }
// public static Task<MqttClientPublishResult> PublishAsync(this IMqttServer server, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
// {
// if (server == null) throw new ArgumentNullException(nameof(server));
// if (topic == null) throw new ArgumentNullException(nameof(topic));
//
// return server.PublishAsync(builder => builder
// .WithTopic(topic)
// .WithPayload(payload)
// .WithQualityOfServiceLevel(qualityOfServiceLevel));
// }
// public static Task<MqttClientPublishResult> PublishAsync(this IMqttServer server, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain)
// {
// if (server == null) throw new ArgumentNullException(nameof(server));
// if (topic == null) throw new ArgumentNullException(nameof(topic));
//
// return server.PublishAsync(builder => builder
// .WithTopic(topic)
// .WithPayload(payload)
// .WithQualityOfServiceLevel(qualityOfServiceLevel)
// .WithRetainFlag(retain));
// }
// public static Task<MqttClientPublishResult> PublishAsync(this IMqttServer server, Func<MqttApplicationMessageBuilder, MqttApplicationMessageBuilder> builder, CancellationToken cancellationToken)
// {
// if (server == null) throw new ArgumentNullException(nameof(server));
//
// var message = builder(new MqttApplicationMessageBuilder()).Build();
// return server.PublishAsync(message, cancellationToken);
// }
//
// public static Task<MqttClientPublishResult> PublishAsync(this IMqttServer server, Func<MqttApplicationMessageBuilder, MqttApplicationMessageBuilder> builder)
// {
// if (server == null) throw new ArgumentNullException(nameof(server));
//
// var message = builder(new MqttApplicationMessageBuilder()).Build();
// return server.PublishAsync(message, CancellationToken.None);
// }
} }
} }
@@ -17,9 +17,5 @@ namespace MQTTnet.Server
public TimeSpan DefaultCommunicationTimeout { get; set; } = TimeSpan.FromSeconds(15); public TimeSpan DefaultCommunicationTimeout { get; set; } = TimeSpan.FromSeconds(15);
public TimeSpan KeepAliveMonitorInterval { get; set; } = TimeSpan.FromMilliseconds(500); public TimeSpan KeepAliveMonitorInterval { get; set; } = TimeSpan.FromMilliseconds(500);
public IMqttServerStorage Storage { get; set; }
public IMqttRetainedMessagesManager RetainedMessagesManager { get; set; } = new MqttRetainedMessagesManager();
} }
} }
@@ -140,18 +140,6 @@ namespace MQTTnet.Server
return this; return this;
} }
#endif #endif
public MqttServerOptionsBuilder WithStorage(IMqttServerStorage value)
{
_options.Storage = value;
return this;
}
public MqttServerOptionsBuilder WithRetainedMessagesManager(IMqttRetainedMessagesManager value)
{
_options.RetainedMessagesManager = value;
return this;
}
// public MqttServerOptionsBuilder WithApplicationMessageInterceptor(IMqttServerApplicationMessageInterceptor value) // public MqttServerOptionsBuilder WithApplicationMessageInterceptor(IMqttServerApplicationMessageInterceptor value)
// { // {
@@ -3,7 +3,7 @@ using MQTTnet.Packets;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
public sealed class MqttApplicationMessageResponse public sealed class PublishResponse
{ {
public MqttApplicationMessageResponseReasonCode ReasonCode { get; set; } = MqttApplicationMessageResponseReasonCode.Success; public MqttApplicationMessageResponseReasonCode ReasonCode { get; set; } = MqttApplicationMessageResponseReasonCode.Success;
@@ -7,9 +7,9 @@ namespace MQTTnet.Server
{ {
public sealed class MqttClientStatus : IMqttClientStatus public sealed class MqttClientStatus : IMqttClientStatus
{ {
readonly MqttClientConnection _connection; readonly MqttClient _connection;
public MqttClientStatus(MqttClientConnection connection) public MqttClientStatus(MqttClient connection)
{ {
_connection = connection ?? throw new ArgumentNullException(nameof(connection)); _connection = connection ?? throw new ArgumentNullException(nameof(connection));
} }
@@ -9,9 +9,9 @@ namespace MQTTnet.Server
{ {
public sealed class MqttSessionStatus : IMqttSessionStatus public sealed class MqttSessionStatus : IMqttSessionStatus
{ {
readonly MqttClientSession _session; readonly MqttSession _session;
public MqttSessionStatus(MqttClientSession session) public MqttSessionStatus(MqttSession session)
{ {
_session = session ?? throw new ArgumentNullException(nameof(session)); _session = session ?? throw new ArgumentNullException(nameof(session));
} }
@@ -1,19 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using MQTTnet.Diagnostics;
namespace MQTTnet.Server
{
public interface IMqttRetainedMessagesManager
{
Task Start(MqttServerOptions options, IMqttNetLogger logger);
Task LoadMessagesAsync();
Task ClearMessagesAsync();
Task HandleMessageAsync(string clientId, MqttApplicationMessage applicationMessage);
Task<IList<MqttApplicationMessage>> GetMessagesAsync();
}
}
@@ -1,35 +0,0 @@
using System;
using System.Collections.Generic;
using MQTTnet.Packets;
namespace MQTTnet.Server
{
public interface IMqttServerPersistedSession
{
/// <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; }
IDictionary<object, object> Items { get; }
IList<MqttTopicFilter> Subscriptions { get; }
/// <summary>
/// Gets the last will message.
/// In MQTT, you use the last will message feature to notify other clients about an ungracefully disconnected client.
/// </summary>
MqttApplicationMessage WillMessage { get; }
/// <summary>
/// Gets the will delay interval.
/// This is the time between the client disconnect and the time the will message will be sent.
/// </summary>
uint? WillDelayInterval { get; }
DateTime? SessionExpiryTimestamp { get; }
IList<MqttQueuedApplicationMessage> PendingApplicationMessages { get; }
}
}
@@ -1,11 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MQTTnet.Server
{
public interface IMqttServerPersistedSessionsStorage
{
Task<IList<IMqttServerPersistedSession>> LoadPersistedSessionsAsync();
}
}
@@ -1,12 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MQTTnet.Server
{
public interface IMqttServerStorage
{
Task SaveRetainedMessagesAsync(IList<MqttApplicationMessage> messages);
Task<IList<MqttApplicationMessage>> LoadRetainedMessagesAsync();
}
}
@@ -4,7 +4,7 @@ using MQTTnet.Protocol;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
public sealed class MqttSubscribeResponse public sealed class SubscribeResponse
{ {
/// <summary> /// <summary>
/// Gets or sets the reason code which is sent to the client. /// Gets or sets the reason code which is sent to the client.
@@ -4,7 +4,7 @@ using MQTTnet.Protocol;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
public sealed class MqttUnsubscribeResponse public sealed class UnsubscribeResponse
{ {
/// <summary> /// <summary>
/// Gets or sets the reason code which is sent to the client. /// Gets or sets the reason code which is sent to the client.
@@ -110,7 +110,7 @@ namespace MQTTnet.Tests.Client
await Task.Delay(500); await Task.Delay(500);
Assert.AreEqual(0, (await server.GetClientStatusAsync()).Count); Assert.AreEqual(0, (await server.GetClientsAsync()).Count);
} }
} }
@@ -27,7 +27,7 @@ namespace MQTTnet.Tests.Client
var client = await testEnvironment.ConnectLowLevelClient(); var client = await testEnvironment.ConnectLowLevelClient();
var i = 0; var i = 0;
server.InterceptingClientPublishAsync += c => server.InterceptingPublishAsync += c =>
{ {
i++; i++;
return Task.CompletedTask; return Task.CompletedTask;
@@ -766,8 +766,8 @@ namespace MQTTnet.Tests.Client
await Task.Delay(500); await Task.Delay(500);
var clientStatus = await testEnvironment.Server.GetClientStatusAsync(); var clientStatus = await testEnvironment.Server.GetClientsAsync();
var sessionStatus = await testEnvironment.Server.GetSessionStatusAsync(); var sessionStatus = await testEnvironment.Server.GetSessionsAsync();
for (var i = 0; i < 98; i++) for (var i = 0; i < 98; i++)
{ {
@@ -1,17 +1,14 @@
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Client; using MQTTnet.Client;
using MQTTnet.Formatter; using MQTTnet.Formatter;
using MQTTnet.Tests.Mockups;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MQTTnet.Tests.MQTTv5 namespace MQTTnet.Tests.MQTTv5
{ {
[TestClass] [TestClass]
public class Server_Tests : BaseTestClass public sealed class Server_Tests : BaseTestClass
{ {
public TestContext TestContext { get; set; }
[TestMethod] [TestMethod]
public async Task Will_Message_Send() public async Task Will_Message_Send()
{ {
@@ -46,7 +43,7 @@ namespace MQTTnet.Tests.MQTTv5
[TestMethod] [TestMethod]
public async Task Validate_IsSessionPresent() public async Task Validate_IsSessionPresent()
{ {
using (var testEnvironment = new TestEnvironment(TestContext)) using (var testEnvironment = CreateTestEnvironment())
{ {
// Create server with persistent sessions enabled // Create server with persistent sessions enabled
@@ -84,7 +81,6 @@ namespace MQTTnet.Tests.MQTTv5
.WithClientId(ClientId) .WithClientId(ClientId)
.Build(); .Build();
var result = await client2.ConnectAsync(options).ConfigureAwait(false); var result = await client2.ConnectAsync(options).ConfigureAwait(false);
await client2.DisconnectAsync(); await client2.DisconnectAsync();
@@ -181,7 +181,7 @@ namespace MQTTnet.Tests.Mockups
Server = Factory.CreateMqttServer(options, ServerLogger); Server = Factory.CreateMqttServer(options, ServerLogger);
Server.ValidatingClientConnectionAsync += e => Server.ValidatingConnectionAsync += e =>
{ {
if (TestContext != null) if (TestContext != null)
{ {
@@ -1,22 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using MQTTnet.Server;
namespace MQTTnet.Tests.Mockups
{
public class TestServerStorage : IMqttServerStorage
{
public IList<MqttApplicationMessage> Messages = new List<MqttApplicationMessage>();
public Task SaveRetainedMessagesAsync(IList<MqttApplicationMessage> messages)
{
Messages = messages;
return Task.CompletedTask;
}
public Task<IList<MqttApplicationMessage>> LoadRetainedMessagesAsync()
{
return Task.FromResult(Messages);
}
}
}
@@ -34,7 +34,7 @@ namespace MQTTnet.Tests.Server
var disconnectedMre = new ManualResetEventSlim(); var disconnectedMre = new ManualResetEventSlim();
var server = await testEnvironment.StartServer(); var server = await testEnvironment.StartServer();
server.ValidatingClientConnectionAsync += e => server.ValidatingConnectionAsync += e =>
{ {
if (string.IsNullOrEmpty(e.ClientId)) if (string.IsNullOrEmpty(e.ClientId))
{ {
@@ -18,7 +18,7 @@ namespace MQTTnet.Tests.Server
{ {
var server = await testEnvironment.StartServer(); var server = await testEnvironment.StartServer();
MqttServerClientConnectedEventArgs eventArgs = null; ClientConnectedEventArgs eventArgs = null;
server.ClientConnectedAsync += e => server.ClientConnectedAsync += e =>
{ {
eventArgs = e; eventArgs = e;
@@ -45,7 +45,7 @@ namespace MQTTnet.Tests.Server
{ {
var server = await testEnvironment.StartServer(); var server = await testEnvironment.StartServer();
MqttServerClientDisconnectedEventArgs eventArgs = null; ClientDisconnectedEventArgs eventArgs = null;
server.ClientDisconnectedAsync += e => server.ClientDisconnectedAsync += e =>
{ {
eventArgs = e; eventArgs = e;
@@ -72,7 +72,7 @@ namespace MQTTnet.Tests.Server
{ {
var server = await testEnvironment.StartServer(); var server = await testEnvironment.StartServer();
MqttServerClientSubscribedTopicEventArgs eventArgs = null; ClientSubscribedTopicEventArgs eventArgs = null;
server.ClientSubscribedTopicAsync += e => server.ClientSubscribedTopicAsync += e =>
{ {
eventArgs = e; eventArgs = e;
@@ -99,7 +99,7 @@ namespace MQTTnet.Tests.Server
{ {
var server = await testEnvironment.StartServer(); var server = await testEnvironment.StartServer();
MqttServerClientUnsubscribedTopicEventArgs eventArgs = null; ClientUnsubscribedTopicEventArgs eventArgs = null;
server.ClientUnsubscribedTopicAsync += e => server.ClientUnsubscribedTopicAsync += e =>
{ {
eventArgs = e; eventArgs = e;
@@ -125,8 +125,8 @@ namespace MQTTnet.Tests.Server
{ {
var server = await testEnvironment.StartServer(); var server = await testEnvironment.StartServer();
InterceptingMqttClientPublishEventArgs eventArgs = null; InterceptingPublishEventArgs eventArgs = null;
server.InterceptingClientPublishAsync += e => server.InterceptingPublishAsync += e =>
{ {
eventArgs = e; eventArgs = e;
return Task.CompletedTask; return Task.CompletedTask;
+22 -17
View File
@@ -512,7 +512,7 @@ namespace MQTTnet.Tests.Server
await Task.Delay(1000); await Task.Delay(1000);
var retainedMessages = await server.GetRetainedApplicationMessagesAsync(); var retainedMessages = await server.GetRetainedMessagesAsync();
Assert.AreEqual(ClientCount, retainedMessages.Count); Assert.AreEqual(ClientCount, retainedMessages.Count);
@@ -530,7 +530,7 @@ namespace MQTTnet.Tests.Server
{ {
var server = await testEnvironment.StartServer(); var server = await testEnvironment.StartServer();
server.InterceptingClientPublishAsync += e => server.InterceptingPublishAsync += e =>
{ {
e.ApplicationMessage = new MqttApplicationMessage {Topic = "new_topic"}; e.ApplicationMessage = new MqttApplicationMessage {Topic = "new_topic"};
@@ -557,19 +557,24 @@ namespace MQTTnet.Tests.Server
[TestMethod] [TestMethod]
public async Task Persist_Retained_Message() public async Task Persist_Retained_Message()
{ {
var serverStorage = new TestServerStorage();
using (var testEnvironment = CreateTestEnvironment()) using (var testEnvironment = CreateTestEnvironment())
{ {
await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithStorage(serverStorage)); List<MqttApplicationMessage> savedRetainedMessages = null;
var s = await testEnvironment.StartServer();
s.RetainedMessageChangedAsync += e =>
{
savedRetainedMessages = e.StoredRetainedMessages;
return Task.CompletedTask;
};
var c1 = await testEnvironment.ConnectClient(); var c1 = await testEnvironment.ConnectClient();
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build()); await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
await Task.Delay(500); await Task.Delay(500);
Assert.AreEqual(1, serverStorage.Messages.Count); Assert.AreEqual(1, savedRetainedMessages?.Count);
} }
} }
@@ -617,7 +622,7 @@ namespace MQTTnet.Tests.Server
using (var testEnvironment = CreateTestEnvironment()) using (var testEnvironment = CreateTestEnvironment())
{ {
var server = await testEnvironment.StartServer(); var server = await testEnvironment.StartServer();
server.InterceptingClientPublishAsync += e => server.InterceptingPublishAsync += e =>
{ {
e.ApplicationMessage.Payload = Encoding.ASCII.GetBytes("extended"); e.ApplicationMessage.Payload = Encoding.ASCII.GetBytes("extended");
return Task.CompletedTask; return Task.CompletedTask;
@@ -698,7 +703,7 @@ namespace MQTTnet.Tests.Server
var server = await testEnvironment.StartServer(); var server = await testEnvironment.StartServer();
server.ValidatingClientConnectionAsync += e => server.ValidatingConnectionAsync += e =>
{ {
e.ReasonCode = MqttConnectReasonCode.NotAuthorized; e.ReasonCode = MqttConnectReasonCode.NotAuthorized;
return Task.CompletedTask; return Task.CompletedTask;
@@ -711,7 +716,7 @@ namespace MQTTnet.Tests.Server
Dictionary<string, bool> _connected; Dictionary<string, bool> _connected;
void ConnectionValidationHandler(ValidatingMqttClientConnectionEventArgs eventArgs) void ConnectionValidationHandler(ValidatingConnectionEventArgs eventArgs)
{ {
if (_connected.ContainsKey(eventArgs.ClientId)) if (_connected.ContainsKey(eventArgs.ClientId))
{ {
@@ -734,7 +739,7 @@ namespace MQTTnet.Tests.Server
var server = await testEnvironment.StartServer(); var server = await testEnvironment.StartServer();
server.ValidatingClientConnectionAsync += e => server.ValidatingConnectionAsync += e =>
{ {
ConnectionValidationHandler(e); ConnectionValidationHandler(e);
return Task.CompletedTask; return Task.CompletedTask;
@@ -923,12 +928,12 @@ namespace MQTTnet.Tests.Server
var clientOptions = new MqttClientOptionsBuilder(); var clientOptions = new MqttClientOptionsBuilder();
var c1 = await testEnvironment.ConnectClient(clientOptions); var c1 = await testEnvironment.ConnectClient(clientOptions);
await Task.Delay(500); await Task.Delay(500);
Assert.AreEqual(1, (await server.GetClientStatusAsync()).Count); Assert.AreEqual(1, (await server.GetClientsAsync()).Count);
await c1.DisconnectAsync(); await c1.DisconnectAsync();
await Task.Delay(500); await Task.Delay(500);
Assert.AreEqual(0, (await server.GetClientStatusAsync()).Count); Assert.AreEqual(0, (await server.GetClientsAsync()).Count);
} }
} }
@@ -965,7 +970,7 @@ namespace MQTTnet.Tests.Server
{ {
var server = await testEnvironment.StartServer(); var server = await testEnvironment.StartServer();
server.InterceptingClientSubscriptionAsync += e => server.InterceptingSubscriptionAsync += e =>
{ {
// This should lead to no subscriptions for "n" at all. So also no sending of retained messages. // This should lead to no subscriptions for "n" at all. So also no sending of retained messages.
if (e.TopicFilter.Topic == "n") if (e.TopicFilter.Topic == "n")
@@ -1021,7 +1026,7 @@ namespace MQTTnet.Tests.Server
await client1.DisconnectAsync(); await client1.DisconnectAsync();
await Task.Delay(500); await Task.Delay(500);
var clientStatus = await server.GetClientStatusAsync(); var clientStatus = await server.GetClientsAsync();
Assert.AreEqual(0, clientStatus.Count); Assert.AreEqual(0, clientStatus.Count);
var client2 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("b")); var client2 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("b"));
@@ -1032,8 +1037,8 @@ namespace MQTTnet.Tests.Server
await Task.Delay(500); await Task.Delay(500);
clientStatus = await server.GetClientStatusAsync(); clientStatus = await server.GetClientsAsync();
var sessionStatus = await server.GetSessionStatusAsync(); var sessionStatus = await server.GetSessionsAsync();
Assert.AreEqual(0, clientStatus.Count); Assert.AreEqual(0, clientStatus.Count);
Assert.AreEqual(2, sessionStatus.Count); Assert.AreEqual(2, sessionStatus.Count);
@@ -2,6 +2,7 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Diagnostics;
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Protocol; using MQTTnet.Protocol;
using MQTTnet.Server; using MQTTnet.Server;
@@ -12,19 +13,37 @@ namespace MQTTnet.Tests.Server
[TestClass] [TestClass]
public sealed class MqttSubscriptionsManager_Tests : BaseTestClass public sealed class MqttSubscriptionsManager_Tests : BaseTestClass
{ {
MqttClientSubscriptionsManager _subscriptionsManager;
[TestInitialize]
public void TestInitialize()
{
var logger = new TestLogger();
var options = new MqttServerOptions();
var retainedMessagesManager = new MqttRetainedMessagesManager(new MqttServerEventContainer(), logger);
var eventContainer = new MqttServerEventContainer();
var session = new MqttSession(
"",
new ConcurrentDictionary<object, object>(),
options,
eventContainer,
retainedMessagesManager,
new MqttClientSessionsManager(options, retainedMessagesManager, eventContainer, logger));
_subscriptionsManager = new MqttClientSubscriptionsManager(session, new MqttServerOptions(), new MqttServerEventContainer(),
new MqttRetainedMessagesManager(new MqttServerEventContainer(), new MqttNetNullLogger()));
}
[TestMethod] [TestMethod]
public async Task MqttSubscriptionsManager_SubscribeSingleSuccess() public async Task MqttSubscriptionsManager_SubscribeSingleSuccess()
{ {
var s = CreateSession();
var sm = new MqttClientSubscriptionsManager(s, new MqttServerOptions(), new MqttServerEventContainer(), new MqttRetainedMessagesManager());
var sp = new MqttSubscribePacket(); var sp = new MqttSubscribePacket();
sp.TopicFilters.Add(new MqttTopicFilterBuilder().WithTopic("A/B/C").Build()); sp.TopicFilters.Add(new MqttTopicFilterBuilder().WithTopic("A/B/C").Build());
await sm.Subscribe(sp, CancellationToken.None); await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
var result = sm.CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, ""); var result = _subscriptionsManager.CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "");
Assert.IsTrue(result.IsSubscribed); Assert.IsTrue(result.IsSubscribed);
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtMostOnce); Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtMostOnce);
} }
@@ -32,16 +51,12 @@ namespace MQTTnet.Tests.Server
[TestMethod] [TestMethod]
public async Task MqttSubscriptionsManager_SubscribeDifferentQoSSuccess() public async Task MqttSubscriptionsManager_SubscribeDifferentQoSSuccess()
{ {
var s = CreateSession();
var sm = new MqttClientSubscriptionsManager(s, new MqttServerOptions(), new MqttServerEventContainer(), new MqttRetainedMessagesManager());
var sp = new MqttSubscribePacket(); var sp = new MqttSubscribePacket();
sp.TopicFilters.Add(new MqttTopicFilter { Topic = "A/B/C", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }); sp.TopicFilters.Add(new MqttTopicFilter {Topic = "A/B/C", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce});
await sm.Subscribe(sp, CancellationToken.None); await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
var result = sm.CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.ExactlyOnce, ""); var result = _subscriptionsManager.CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.ExactlyOnce, "");
Assert.IsTrue(result.IsSubscribed); Assert.IsTrue(result.IsSubscribed);
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtMostOnce); Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtMostOnce);
} }
@@ -49,17 +64,13 @@ namespace MQTTnet.Tests.Server
[TestMethod] [TestMethod]
public async Task MqttSubscriptionsManager_SubscribeTwoTimesSuccess() public async Task MqttSubscriptionsManager_SubscribeTwoTimesSuccess()
{ {
var s = CreateSession();
var sm = new MqttClientSubscriptionsManager(s, new MqttServerOptions(), new MqttServerEventContainer(), new MqttRetainedMessagesManager());
var sp = new MqttSubscribePacket(); var sp = new MqttSubscribePacket();
sp.TopicFilters.Add(new MqttTopicFilter { Topic = "#", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }); sp.TopicFilters.Add(new MqttTopicFilter {Topic = "#", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce});
sp.TopicFilters.Add(new MqttTopicFilter { Topic = "A/B/C", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }); sp.TopicFilters.Add(new MqttTopicFilter {Topic = "A/B/C", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce});
await sm.Subscribe(sp, CancellationToken.None); await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
var result = sm.CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.ExactlyOnce, ""); var result = _subscriptionsManager.CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.ExactlyOnce, "");
Assert.IsTrue(result.IsSubscribed); Assert.IsTrue(result.IsSubscribed);
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtLeastOnce); Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtLeastOnce);
} }
@@ -67,53 +78,29 @@ namespace MQTTnet.Tests.Server
[TestMethod] [TestMethod]
public async Task MqttSubscriptionsManager_SubscribeSingleNoSuccess() public async Task MqttSubscriptionsManager_SubscribeSingleNoSuccess()
{ {
var s = CreateSession();
var sm = new MqttClientSubscriptionsManager(s, new MqttServerOptions(), new MqttServerEventContainer(), new MqttRetainedMessagesManager());
var sp = new MqttSubscribePacket(); var sp = new MqttSubscribePacket();
sp.TopicFilters.Add(new MqttTopicFilterBuilder().WithTopic("A/B/C").Build()); sp.TopicFilters.Add(new MqttTopicFilterBuilder().WithTopic("A/B/C").Build());
await sm.Subscribe(sp, CancellationToken.None); await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
Assert.IsFalse(sm.CheckSubscriptions("A/B/X", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed); Assert.IsFalse(_subscriptionsManager.CheckSubscriptions("A/B/X", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed);
} }
[TestMethod] [TestMethod]
public async Task MqttSubscriptionsManager_SubscribeAndUnsubscribeSingle() public async Task MqttSubscriptionsManager_SubscribeAndUnsubscribeSingle()
{ {
var s = CreateSession();
var sm = new MqttClientSubscriptionsManager(s, new MqttServerOptions(), new MqttServerEventContainer(), new MqttRetainedMessagesManager());
var sp = new MqttSubscribePacket(); var sp = new MqttSubscribePacket();
sp.TopicFilters.Add(new MqttTopicFilterBuilder().WithTopic("A/B/C").Build()); sp.TopicFilters.Add(new MqttTopicFilterBuilder().WithTopic("A/B/C").Build());
await sm.Subscribe(sp, CancellationToken.None); await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
Assert.IsTrue(sm.CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed); Assert.IsTrue(_subscriptionsManager.CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed);
var up = new MqttUnsubscribePacket(); var up = new MqttUnsubscribePacket();
up.TopicFilters.Add("A/B/C"); up.TopicFilters.Add("A/B/C");
await sm.Unsubscribe(up, CancellationToken.None); await _subscriptionsManager.Unsubscribe(up, CancellationToken.None);
Assert.IsFalse(sm.CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed); Assert.IsFalse(_subscriptionsManager.CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed);
}
MqttClientSession CreateSession()
{
var logger = new TestLogger();
var options = new MqttServerOptions();
var retainedMessagesManager = new MqttRetainedMessagesManager();
var eventContainer = new MqttServerEventContainer();
return new MqttClientSession(
"",
new ConcurrentDictionary<object, object>(),
options,
eventContainer,
retainedMessagesManager,
new MqttClientSessionsManager(options, retainedMessagesManager, eventContainer, logger));
} }
} }
} }
@@ -61,7 +61,7 @@ namespace MQTTnet.Tests.Server
var server = await testEnvironment.StartServer(); var server = await testEnvironment.StartServer();
server.ValidatingClientConnectionAsync += e => server.ValidatingConnectionAsync += e =>
{ {
if (e.Username != "UserName1") if (e.Username != "UserName1")
{ {
@@ -19,7 +19,7 @@ namespace MQTTnet.Tests.Server
var server = await testEnvironment.StartServer(); var server = await testEnvironment.StartServer();
server.ValidatingClientConnectionAsync += e => server.ValidatingConnectionAsync += e =>
{ {
e.ReasonCode = MqttConnectReasonCode.ServerMoved; e.ReasonCode = MqttConnectReasonCode.ServerMoved;
e.ServerReference = "new_server"; e.ServerReference = "new_server";
@@ -20,7 +20,7 @@ namespace MQTTnet.Tests.Server
{ {
var server = await testEnvironment.StartServer(); var server = await testEnvironment.StartServer();
server.ValidatingClientConnectionAsync += e => server.ValidatingConnectionAsync += e =>
{ {
// Don't validate anything. Just set some session items. // Don't validate anything. Just set some session items.
e.SessionItems["can_subscribe_x"] = true; e.SessionItems["can_subscribe_x"] = true;
@@ -29,7 +29,7 @@ namespace MQTTnet.Tests.Server
return Task.CompletedTask; return Task.CompletedTask;
}; };
server.InterceptingClientSubscriptionAsync += e => server.InterceptingSubscriptionAsync += e =>
{ {
if (e.TopicFilter.Topic == "x") if (e.TopicFilter.Topic == "x")
{ {
@@ -42,7 +42,7 @@ namespace MQTTnet.Tests.Server
return Task.CompletedTask; return Task.CompletedTask;
}; };
server.InterceptingClientPublishAsync += e => server.InterceptingPublishAsync += e =>
{ {
e.ApplicationMessage.Payload = e.ApplicationMessage.Payload =
Encoding.UTF8.GetBytes( Encoding.UTF8.GetBytes(
@@ -80,7 +80,7 @@ namespace MQTTnet.Tests.Server
{ {
var server = await testEnvironment.StartServer(); var server = await testEnvironment.StartServer();
server.ValidatingClientConnectionAsync += e => server.ValidatingConnectionAsync += e =>
{ {
// Don't validate anything. Just set some session items. // Don't validate anything. Just set some session items.
e.SessionItems["can_subscribe_x"] = true; e.SessionItems["can_subscribe_x"] = true;
@@ -91,7 +91,7 @@ namespace MQTTnet.Tests.Server
await testEnvironment.ConnectClient(); await testEnvironment.ConnectClient();
var sessionStatus = await testEnvironment.Server.GetSessionStatusAsync(); var sessionStatus = await testEnvironment.Server.GetSessionsAsync();
var session = sessionStatus.First(); var session = sessionStatus.First();
Assert.AreEqual(true, session.Items["can_subscribe_x"]); Assert.AreEqual(true, session.Items["can_subscribe_x"]);
@@ -128,7 +128,7 @@ namespace MQTTnet.Tests.Server
var client = await testEnvironment.ConnectClient(); var client = await testEnvironment.ConnectClient();
// Arrange session status tracking. // Arrange session status tracking.
var status = await server.GetSessionStatusAsync(); var status = await server.GetSessionsAsync();
var clientStatus = status[0]; var clientStatus = status[0];
var deletedEventFired = false; var deletedEventFired = false;
@@ -158,7 +158,7 @@ namespace MQTTnet.Tests.Server
var messageReceivedHandler = testEnvironment.CreateApplicationMessageHandler(client); var messageReceivedHandler = testEnvironment.CreateApplicationMessageHandler(client);
// Arrange session status tracking. // Arrange session status tracking.
var status = await server.GetSessionStatusAsync(); var status = await server.GetSessionsAsync();
var clientStatus = status[0]; var clientStatus = status[0];
// Act: Inject the application message. // Act: Inject the application message.
+12 -12
View File
@@ -23,8 +23,8 @@ namespace MQTTnet.Tests.Server
await Task.Delay(500); await Task.Delay(500);
var clientStatus = await server.GetClientStatusAsync(); var clientStatus = await server.GetClientsAsync();
var sessionStatus = await server.GetSessionStatusAsync(); var sessionStatus = await server.GetSessionsAsync();
Assert.AreEqual(2, clientStatus.Count); Assert.AreEqual(2, clientStatus.Count);
Assert.AreEqual(2, sessionStatus.Count); Assert.AreEqual(2, sessionStatus.Count);
@@ -37,8 +37,8 @@ namespace MQTTnet.Tests.Server
await Task.Delay(500); await Task.Delay(500);
clientStatus = await server.GetClientStatusAsync(); clientStatus = await server.GetClientsAsync();
sessionStatus = await server.GetSessionStatusAsync(); sessionStatus = await server.GetSessionsAsync();
Assert.AreEqual(0, clientStatus.Count); Assert.AreEqual(0, clientStatus.Count);
Assert.AreEqual(0, sessionStatus.Count); Assert.AreEqual(0, sessionStatus.Count);
@@ -56,7 +56,7 @@ namespace MQTTnet.Tests.Server
await Task.Delay(1000); await Task.Delay(1000);
var clientStatus = await server.GetClientStatusAsync(); var clientStatus = await server.GetClientsAsync();
Assert.AreEqual(1, clientStatus.Count); Assert.AreEqual(1, clientStatus.Count);
Assert.IsTrue(clientStatus.Any(s => s.ClientId == c1.Options.ClientId)); Assert.IsTrue(clientStatus.Any(s => s.ClientId == c1.Options.ClientId));
@@ -67,7 +67,7 @@ namespace MQTTnet.Tests.Server
Assert.IsFalse(c1.IsConnected); Assert.IsFalse(c1.IsConnected);
clientStatus = await server.GetClientStatusAsync(); clientStatus = await server.GetClientsAsync();
Assert.AreEqual(0, clientStatus.Count); Assert.AreEqual(0, clientStatus.Count);
} }
@@ -87,8 +87,8 @@ namespace MQTTnet.Tests.Server
await Task.Delay(500); await Task.Delay(500);
var clientStatus = await server.GetClientStatusAsync(); var clientStatus = await server.GetClientsAsync();
var sessionStatus = await server.GetSessionStatusAsync(); var sessionStatus = await server.GetSessionsAsync();
Assert.AreEqual(1, clientStatus.Count); Assert.AreEqual(1, clientStatus.Count);
Assert.AreEqual(2, sessionStatus.Count); Assert.AreEqual(2, sessionStatus.Count);
@@ -97,8 +97,8 @@ namespace MQTTnet.Tests.Server
await Task.Delay(500); await Task.Delay(500);
clientStatus = await server.GetClientStatusAsync(); clientStatus = await server.GetClientsAsync();
sessionStatus = await server.GetSessionStatusAsync(); sessionStatus = await server.GetSessionsAsync();
Assert.AreEqual(0, clientStatus.Count); Assert.AreEqual(0, clientStatus.Count);
Assert.AreEqual(2, sessionStatus.Count); Assert.AreEqual(2, sessionStatus.Count);
@@ -119,7 +119,7 @@ namespace MQTTnet.Tests.Server
await c1.PublishAsync("a"); await c1.PublishAsync("a");
await Task.Delay(50); await Task.Delay(50);
var clientStatus = await server.GetClientStatusAsync(); var clientStatus = await server.GetClientsAsync();
Assert.AreEqual(i, clientStatus.First().SentApplicationMessagesCount); Assert.AreEqual(i, clientStatus.First().SentApplicationMessagesCount);
Assert.AreEqual(0, clientStatus.First().ReceivedApplicationMessagesCount); Assert.AreEqual(0, clientStatus.First().ReceivedApplicationMessagesCount);
} }
@@ -143,7 +143,7 @@ namespace MQTTnet.Tests.Server
await Task.Delay(500); await Task.Delay(500);
var clientStatus = await server.GetClientStatusAsync(); var clientStatus = await server.GetClientsAsync();
Assert.AreEqual(i, clientStatus.First().SentApplicationMessagesCount, "SAMC invalid!"); Assert.AreEqual(i, clientStatus.First().SentApplicationMessagesCount, "SAMC invalid!");
@@ -20,7 +20,7 @@ namespace MQTTnet.Tests.Server
{ {
var server = await testEnvironment.StartServer(); var server = await testEnvironment.StartServer();
server.InterceptingClientSubscriptionAsync += e => server.InterceptingSubscriptionAsync += e =>
{ {
// Set the topic to "a" regards what the client wants to subscribe. // Set the topic to "a" regards what the client wants to subscribe.
e.TopicFilter.Topic = "a"; e.TopicFilter.Topic = "a";
@@ -290,7 +290,7 @@ namespace MQTTnet.Tests.Server
{ {
var server = await testEnvironment.StartServer(); var server = await testEnvironment.StartServer();
server.InterceptingClientSubscriptionAsync += e => server.InterceptingSubscriptionAsync += e =>
{ {
if (e.TopicFilter.Topic == "not_allowed_topic") if (e.TopicFilter.Topic == "not_allowed_topic")
{ {
-33
View File
@@ -134,39 +134,6 @@ namespace MQTTnet.TestApp.NetCore
} }
} }
public class RetainedMessageHandler : IMqttServerStorage
{
const string Filename = "C:\\MQTT\\RetainedMessages.json";
public Task SaveRetainedMessagesAsync(IList<MqttApplicationMessage> messages)
{
var directory = Path.GetDirectoryName(Filename);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
File.WriteAllText(Filename, JsonConvert.SerializeObject(messages));
return Task.FromResult(0);
}
public Task<IList<MqttApplicationMessage>> LoadRetainedMessagesAsync()
{
IList<MqttApplicationMessage> retainedMessages;
if (File.Exists(Filename))
{
var json = File.ReadAllText(Filename);
retainedMessages = JsonConvert.DeserializeObject<List<MqttApplicationMessage>>(json);
}
else
{
retainedMessages = new List<MqttApplicationMessage>();
}
return Task.FromResult(retainedMessages);
}
}
public class WikiCode public class WikiCode
{ {
public void Code() public void Code()
+47 -8
View File
@@ -1,10 +1,14 @@
using System; using System;
using System.Collections.Generic;
using System.IO;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Diagnostics; using MQTTnet.Diagnostics;
using MQTTnet.Extensions.ManagedClient;
using MQTTnet.Implementations; using MQTTnet.Implementations;
using MQTTnet.Protocol; using MQTTnet.Protocol;
using MQTTnet.Server; using MQTTnet.Server;
using Newtonsoft.Json;
namespace MQTTnet.TestApp.NetCore namespace MQTTnet.TestApp.NetCore
{ {
@@ -36,10 +40,7 @@ namespace MQTTnet.TestApp.NetCore
{ {
try try
{ {
var options = new MqttServerOptions var options = new MqttServerOptions();
{
Storage = new RetainedMessageHandler()
};
// Extend the timestamp for all messages from clients. // Extend the timestamp for all messages from clients.
// Protect several topics from being subscribed from every client. // Protect several topics from being subscribed from every client.
@@ -52,7 +53,45 @@ namespace MQTTnet.TestApp.NetCore
var mqttServer = new MqttFactory().CreateMqttServer(options); var mqttServer = new MqttFactory().CreateMqttServer(options);
mqttServer.InterceptingClientPublishAsync += e => const string Filename = "C:\\MQTT\\RetainedMessages.json";
mqttServer.RetainedMessageChangedAsync += e =>
{
var directory = Path.GetDirectoryName(Filename);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
File.WriteAllText(Filename, JsonConvert.SerializeObject(e.StoredRetainedMessages));
return Task.FromResult(0);
};
mqttServer.RetainedMessagesClearedAsync += e =>
{
File.Delete(Filename);
return Task.FromResult(0);
};
mqttServer.LoadingRetainedMessageAsync += e =>
{
List<MqttApplicationMessage> retainedMessages;
if (File.Exists(Filename))
{
var json = File.ReadAllText(Filename);
retainedMessages = JsonConvert.DeserializeObject<List<MqttApplicationMessage>>(json);
}
else
{
retainedMessages = new List<MqttApplicationMessage>();
}
e.StoredRetainedMessages = retainedMessages;
return Task.FromResult(0);
};
mqttServer.InterceptingPublishAsync += e =>
{ {
if (MqttTopicFilterComparer.Compare(e.ApplicationMessage.Topic, "/myTopic/WithTimestamp/#") == MqttTopicFilterCompareResult.IsMatch) if (MqttTopicFilterComparer.Compare(e.ApplicationMessage.Topic, "/myTopic/WithTimestamp/#") == MqttTopicFilterCompareResult.IsMatch)
{ {
@@ -70,7 +109,7 @@ namespace MQTTnet.TestApp.NetCore
return PlatformAbstractionLayer.CompletedTask; return PlatformAbstractionLayer.CompletedTask;
}; };
mqttServer.ValidatingClientConnectionAsync += e => mqttServer.ValidatingConnectionAsync += e =>
{ {
if (e.ClientId == "SpecialClient") if (e.ClientId == "SpecialClient")
{ {
@@ -83,7 +122,7 @@ namespace MQTTnet.TestApp.NetCore
return PlatformAbstractionLayer.CompletedTask; return PlatformAbstractionLayer.CompletedTask;
}; };
mqttServer.InterceptingClientSubscriptionAsync += e => mqttServer.InterceptingSubscriptionAsync += e =>
{ {
if (e.TopicFilter.Topic.StartsWith("admin/foo/bar") && e.ClientId != "theAdmin") if (e.TopicFilter.Topic.StartsWith("admin/foo/bar") && e.ClientId != "theAdmin")
{ {
@@ -99,7 +138,7 @@ namespace MQTTnet.TestApp.NetCore
return PlatformAbstractionLayer.CompletedTask; return PlatformAbstractionLayer.CompletedTask;
}; };
mqttServer.InterceptingClientPublishAsync += e => mqttServer.InterceptingPublishAsync += e =>
{ {
MqttNetConsoleLogger.PrintToConsole( MqttNetConsoleLogger.PrintToConsole(
$"'{e.ClientId}' reported '{e.ApplicationMessage.Topic}' > '{Encoding.UTF8.GetString(e.ApplicationMessage.Payload ?? new byte[0])}'", $"'{e.ClientId}' reported '{e.ApplicationMessage.Topic}' > '{Encoding.UTF8.GetString(e.ApplicationMessage.Payload ?? new byte[0])}'",