mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-20 11:45:43 +08:00
Improve session message injection (#2117)
* Improve session message injection methods * Add more tests for session injection * Change return type from MqttPublishPacket to MqttPacketWithIdentifier * Use dedicated type for MattApplicationMessage injection result * Code review minor fixes * Apply project code style * Update release notes --------- Co-authored-by: Anton Smolkov <anton.smolkov@idp.zyfra.com> Co-authored-by: christian <6939810+chkr1011@users.noreply.github.com>
This commit is contained in:
co-authored by
Anton Smolkov
christian
parent
e7de2b913a
commit
27771f0146
@@ -0,0 +1,19 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Server.Exceptions;
|
||||
|
||||
public class MqttPendingMessagesOverflowException : Exception
|
||||
{
|
||||
public MqttPendingMessagesOverflowException(string sessionId, MqttPendingMessagesOverflowStrategy overflowStrategy) : base(
|
||||
$"Send buffer max pending messages overflow occurred for session '{sessionId}'. Strategy: {overflowStrategy}.")
|
||||
{
|
||||
SessionId = sessionId;
|
||||
OverflowStrategy = overflowStrategy;
|
||||
}
|
||||
|
||||
public MqttPendingMessagesOverflowStrategy OverflowStrategy { get; }
|
||||
|
||||
public string SessionId { get; }
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using System.Collections;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server.Exceptions;
|
||||
|
||||
namespace MQTTnet.Server.Internal;
|
||||
|
||||
@@ -111,10 +112,11 @@ public sealed class MqttSession : IDisposable
|
||||
|
||||
public EnqueueDataPacketResult EnqueueDataPacket(MqttPacketBusItem packetBusItem)
|
||||
{
|
||||
if (_packetBus.ItemsCount(MqttPacketBusPartition.Data) >= _serverOptions.MaxPendingMessagesPerClient)
|
||||
if (PendingDataPacketsCount >= _serverOptions.MaxPendingMessagesPerClient)
|
||||
{
|
||||
if (_serverOptions.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropNewMessage)
|
||||
{
|
||||
packetBusItem.Fail(new MqttPendingMessagesOverflowException(Id, _serverOptions.PendingMessagesOverflowStrategy));
|
||||
return EnqueueDataPacketResult.Dropped;
|
||||
}
|
||||
|
||||
@@ -123,10 +125,15 @@ public sealed class MqttSession : IDisposable
|
||||
// Only drop from the data partition. Dropping from control partition might break the connection
|
||||
// because the client does not receive PINGREQ packets etc. any longer.
|
||||
var firstItem = _packetBus.DropFirstItem(MqttPacketBusPartition.Data);
|
||||
if (firstItem != null && _eventContainer.QueuedApplicationMessageOverwrittenEvent.HasHandlers)
|
||||
if (firstItem != null)
|
||||
{
|
||||
var eventArgs = new QueueMessageOverwrittenEventArgs(Id, firstItem.Packet);
|
||||
_eventContainer.QueuedApplicationMessageOverwrittenEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
firstItem.Fail(new MqttPendingMessagesOverflowException(Id, _serverOptions.PendingMessagesOverflowStrategy));
|
||||
|
||||
if (_eventContainer.QueuedApplicationMessageOverwrittenEvent.HasHandlers)
|
||||
{
|
||||
var eventArgs = new QueueMessageOverwrittenEventArgs(Id, firstItem.Packet);
|
||||
_eventContainer.QueuedApplicationMessageOverwrittenEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +115,12 @@ namespace MQTTnet.Server
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttServerOptionsBuilder WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy value)
|
||||
{
|
||||
_options.PendingMessagesOverflowStrategy = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttServerOptionsBuilder WithoutDefaultEndpoint()
|
||||
{
|
||||
_options.DefaultEndpointOptions.IsEnabled = false;
|
||||
|
||||
@@ -40,22 +40,65 @@ public sealed class MqttSessionStatus
|
||||
return _session.DeleteAsync();
|
||||
}
|
||||
|
||||
public Task DeliverApplicationMessageAsync(MqttApplicationMessage applicationMessage)
|
||||
/// <summary>
|
||||
/// Delivers an application message immediately to the session.
|
||||
/// </summary>
|
||||
/// <param name="applicationMessage">The application message to deliver.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation.
|
||||
/// The result contains the <see cref="InjectMqttApplicationMessageResult"/> that includes the packet identifier of the enqueued message.
|
||||
/// </returns>
|
||||
public async Task<InjectMqttApplicationMessageResult> DeliverApplicationMessageAsync(MqttApplicationMessage applicationMessage)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(applicationMessage);
|
||||
|
||||
var packetBusItem = new MqttPacketBusItem(MqttPublishPacketFactory.Create(applicationMessage));
|
||||
var publishPacket = MqttPublishPacketFactory.Create(applicationMessage);
|
||||
var packetBusItem = new MqttPacketBusItem(publishPacket);
|
||||
_session.EnqueueDataPacket(packetBusItem);
|
||||
|
||||
return packetBusItem.WaitAsync();
|
||||
await packetBusItem.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
var injectResult = new InjectMqttApplicationMessageResult()
|
||||
{
|
||||
PacketIdentifier = publishPacket.PacketIdentifier
|
||||
};
|
||||
|
||||
return injectResult;
|
||||
}
|
||||
|
||||
public Task EnqueueApplicationMessageAsync(MqttApplicationMessage applicationMessage)
|
||||
/// <summary>
|
||||
/// Attempts to enqueue an application message to the session's send buffer.
|
||||
/// </summary>
|
||||
/// <param name="applicationMessage">The application message to enqueue.</param>
|
||||
/// <param name="injectResult"><see cref="InjectMqttApplicationMessageResult"/> that includes the packet identifier of the enqueued message.</param>
|
||||
/// <returns><c>true</c> if the message was successfully enqueued; otherwise, <c>false</c>.</returns>
|
||||
/// <remarks>
|
||||
/// When <see cref="MqttServerOptions.PendingMessagesOverflowStrategy"/> is set to <see cref="MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage"/>,
|
||||
/// this method always returns <c>true</c>.
|
||||
/// However, an existing message in the queue may be <b>dropped later</b> to make room for the newly enqueued message.
|
||||
/// Such dropped messages can be tracked by subscribing to <see cref="MqttServer.QueuedApplicationMessageOverwrittenAsync"/> event.
|
||||
/// </remarks>
|
||||
public bool TryEnqueueApplicationMessage(MqttApplicationMessage applicationMessage, out InjectMqttApplicationMessageResult injectResult)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(applicationMessage);
|
||||
|
||||
_session.EnqueueDataPacket(new MqttPacketBusItem(MqttPublishPacketFactory.Create(applicationMessage)));
|
||||
var publishPacket = MqttPublishPacketFactory.Create(applicationMessage);
|
||||
var enqueueDataPacketResult = _session.EnqueueDataPacket(new MqttPacketBusItem(publishPacket));
|
||||
|
||||
if (enqueueDataPacketResult != EnqueueDataPacketResult.Enqueued)
|
||||
{
|
||||
injectResult = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
injectResult = new InjectMqttApplicationMessageResult() { PacketIdentifier = publishPacket.PacketIdentifier };
|
||||
return true;
|
||||
}
|
||||
|
||||
[Obsolete("This method is obsolete. Use TryEnqueueApplicationMessage instead.")]
|
||||
public Task EnqueueApplicationMessageAsync(MqttApplicationMessage applicationMessage)
|
||||
{
|
||||
TryEnqueueApplicationMessage(applicationMessage, out _);
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user