diff --git a/.github/workflows/ReleaseNotes.md b/.github/workflows/ReleaseNotes.md index fe28bf6d6738303ea0995c56e83828608f99163c..eee876618e7f72dd3683d4cdabc07cb634c8f3e9 100644 --- a/.github/workflows/ReleaseNotes.md +++ b/.github/workflows/ReleaseNotes.md @@ -5,3 +5,4 @@ * [Server] Exposed MQTT v5 sent properties from the affected client in _ClientDisconnectedAsync_ event. * [Server] Fixed wrong client ID passed to _InterceptingUnsubscriptionEventArgs_ (#1631, thanks to @ghord). * [Server] Exposed socket settings for TCP keep alive in TCP options (#1544). +* [Server] Exposed server session items at server level and allow custom session items for injected application messages (#1638). diff --git a/Source/MQTTnet/Adapter/MqttPacketInspector.cs b/Source/MQTTnet/Adapter/MqttPacketInspector.cs index c74114ae1740f756930f6c217e535904d32a4d64..a83de0e2de27c086e9f4b37ff63bb8c9a1147eb9 100644 --- a/Source/MQTTnet/Adapter/MqttPacketInspector.cs +++ b/Source/MQTTnet/Adapter/MqttPacketInspector.cs @@ -82,12 +82,7 @@ namespace MQTTnet.Adapter { try { - var eventArgs = new InspectMqttPacketEventArgs - { - Buffer = buffer, - Direction = direction - }; - + var eventArgs = new InspectMqttPacketEventArgs(direction, buffer); _asyncEvent.InvokeAsync(eventArgs).GetAwaiter().GetResult(); } catch (Exception exception) diff --git a/Source/MQTTnet/Client/Publishing/MqttClientPublishResult.cs b/Source/MQTTnet/Client/Publishing/MqttClientPublishResult.cs index 6fd2dcd89b6c965f72ae7fce6e9af28684316710..c9859d1b621b6de9c242d165bb5a507a80823450 100644 --- a/Source/MQTTnet/Client/Publishing/MqttClientPublishResult.cs +++ b/Source/MQTTnet/Client/Publishing/MqttClientPublishResult.cs @@ -9,9 +9,18 @@ namespace MQTTnet.Client { public sealed class MqttClientPublishResult { + public MqttClientPublishResult(ushort? packetIdentifier, MqttClientPublishReasonCode reasonCode, string reasonString, IReadOnlyCollection userProperties) + { + PacketIdentifier = packetIdentifier; + ReasonCode = reasonCode; + ReasonString = reasonString; + UserProperties = userProperties; + } + /// /// Returns if the overall status of the publish is a success. This can be the reason code _Success_ or - /// _NoMatchingSubscribers_. _NoMatchingSubscribers_ usually indicates only that no other client is interested in the topic but overall transmit + /// _NoMatchingSubscribers_. _NoMatchingSubscribers_ usually indicates only that no other client is interested in the + /// topic but overall transmit /// to the server etc. was a success. /// public bool IsSuccess => ReasonCode == MqttClientPublishReasonCode.Success || ReasonCode == MqttClientPublishReasonCode.NoMatchingSubscribers; @@ -19,19 +28,19 @@ namespace MQTTnet.Client /// /// Gets the packet identifier which was used for this publish. /// - public ushort? PacketIdentifier { get; set; } + public ushort? PacketIdentifier { get; } /// /// Gets or sets the reason code. - /// Hint: MQTT 5 feature only. + /// MQTT 5.0.0+ feature. /// - public MqttClientPublishReasonCode ReasonCode { get; set; } = MqttClientPublishReasonCode.Success; + public MqttClientPublishReasonCode ReasonCode { get; } = MqttClientPublishReasonCode.Success; /// /// Gets or sets the reason string. - /// Hint: MQTT 5 feature only. + /// MQTT 5.0.0+ feature. /// - public string ReasonString { get; set; } + public string ReasonString { get; } /// /// Gets or sets the user properties. @@ -40,8 +49,8 @@ namespace MQTTnet.Client /// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add /// metadata to MQTT messages and pass information between publisher, broker, and subscriber. /// The feature is very similar to the HTTP header concept. - /// Hint: MQTT 5 feature only. + /// MQTT 5.0.0+ feature. /// - public IReadOnlyCollection UserProperties { get; internal set; } + public IReadOnlyCollection UserProperties { get; } } } \ No newline at end of file diff --git a/Source/MQTTnet/Client/Publishing/MqttClientPublishResultFactory.cs b/Source/MQTTnet/Client/Publishing/MqttClientPublishResultFactory.cs index ea5bc2332dec18da279ad0204df311766ad11c05..3f4c6397708912fbd3caf01512e9340bf6193f92 100644 --- a/Source/MQTTnet/Client/Publishing/MqttClientPublishResultFactory.cs +++ b/Source/MQTTnet/Client/Publishing/MqttClientPublishResultFactory.cs @@ -10,25 +10,23 @@ namespace MQTTnet.Client { public sealed class MqttClientPublishResultFactory { - static readonly MqttClientPublishResult EmptySuccessResult = new MqttClientPublishResult(); static readonly IReadOnlyCollection EmptyUserProperties = new List(); + static readonly MqttClientPublishResult AtMostOnceSuccessResult = new MqttClientPublishResult(null, MqttClientPublishReasonCode.Success, null, EmptyUserProperties); public MqttClientPublishResult Create(MqttPubAckPacket pubAckPacket) { // QoS 0 has no response. So we treat it as a success always. if (pubAckPacket == null) { - return EmptySuccessResult; + return AtMostOnceSuccessResult; } - var result = new MqttClientPublishResult - { + var result = new MqttClientPublishResult( + pubAckPacket.PacketIdentifier, // Both enums have the same values. So it can be easily converted. - ReasonCode = (MqttClientPublishReasonCode)(int)pubAckPacket.ReasonCode, - PacketIdentifier = pubAckPacket.PacketIdentifier, - ReasonString = pubAckPacket.ReasonString, - UserProperties = pubAckPacket.UserProperties ?? EmptyUserProperties - }; + (MqttClientPublishReasonCode)(int)pubAckPacket.ReasonCode, + pubAckPacket.ReasonString, + pubAckPacket.UserProperties ?? EmptyUserProperties); return result; } @@ -37,43 +35,28 @@ namespace MQTTnet.Client { if (pubRecPacket == null || pubCompPacket == null) { - return new MqttClientPublishResult - { - ReasonCode = MqttClientPublishReasonCode.UnspecifiedError - }; + var packetIdentifier = pubRecPacket?.PacketIdentifier ?? pubCompPacket?.PacketIdentifier; + return new MqttClientPublishResult(packetIdentifier, MqttClientPublishReasonCode.ImplementationSpecificError, null, EmptyUserProperties); } - MqttClientPublishResult result; - // The PUBCOMP is the last packet in QoS 2. So we use the results from that instead of PUBREC. if (pubCompPacket.ReasonCode == MqttPubCompReasonCode.PacketIdentifierNotFound) { - result = new MqttClientPublishResult - { - PacketIdentifier = pubCompPacket.PacketIdentifier, - ReasonCode = MqttClientPublishReasonCode.UnspecifiedError, - ReasonString = pubCompPacket.ReasonString, - UserProperties = pubCompPacket.UserProperties ?? EmptyUserProperties - }; - - return result; + return new MqttClientPublishResult( + pubCompPacket.PacketIdentifier, + MqttClientPublishReasonCode.UnspecifiedError, + pubCompPacket.ReasonString, + pubCompPacket.UserProperties ?? EmptyUserProperties); } - result = new MqttClientPublishResult - { - PacketIdentifier = pubCompPacket.PacketIdentifier, - ReasonCode = MqttClientPublishReasonCode.Success, - ReasonString = pubCompPacket.ReasonString, - UserProperties = pubCompPacket.UserProperties ?? EmptyUserProperties - }; - + var reasonCode = MqttClientPublishReasonCode.Success; if (pubRecPacket.ReasonCode != MqttPubRecReasonCode.Success) { // Both enums share the same values. - result.ReasonCode = (MqttClientPublishReasonCode)pubRecPacket.ReasonCode; + reasonCode = (MqttClientPublishReasonCode)pubRecPacket.ReasonCode; } - return result; + return new MqttClientPublishResult(pubCompPacket.PacketIdentifier, reasonCode, null, pubCompPacket.UserProperties ?? EmptyUserProperties); } } } \ No newline at end of file diff --git a/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResult.cs b/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResult.cs index 67b526001718fff790fd0db6c098bae73c342ac8..9459679b3ab0f9f1ab13545613d950b6af465ab0 100644 --- a/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResult.cs +++ b/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResult.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using MQTTnet.Packets; @@ -9,23 +10,34 @@ namespace MQTTnet.Client { public sealed class MqttClientSubscribeResult { - public IReadOnlyCollection Items { get; internal set; } + public MqttClientSubscribeResult(ushort packetIdentifier, IReadOnlyCollection items, string reasonString, IReadOnlyCollection userProperties) + { + PacketIdentifier = packetIdentifier; + Items = items ?? throw new ArgumentNullException(nameof(items)); + ReasonString = reasonString; + UserProperties = userProperties ?? throw new ArgumentNullException(nameof(userProperties)); + } + + /// + /// Gets the result for every topic filter item. + /// + public IReadOnlyCollection Items { get; } /// /// Gets the user properties which were part of the SUBACK packet. - /// MQTTv5 only. + /// MQTT 5.0.0+ feature. /// - public IReadOnlyCollection UserProperties { get; internal set; } + public IReadOnlyCollection UserProperties { get; } /// /// Gets the reason string. - /// MQTTv5 only. + /// MQTT 5.0.0+ feature. /// - public string ReasonString { get; internal set; } + public string ReasonString { get; } /// /// Gets the packet identifier which was used. /// - public ushort PacketIdentifier { get; internal set; } + public ushort PacketIdentifier { get; } } } \ No newline at end of file diff --git a/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResultFactory.cs b/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResultFactory.cs index a0613248fbf4296da3fefc0b59b03367c1f7000b..5bb129b44a3c3d436693d94efa6e1190514266d2 100644 --- a/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResultFactory.cs +++ b/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResultFactory.cs @@ -16,42 +16,35 @@ namespace MQTTnet.Client public MqttClientSubscribeResult Create(MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket) { - if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket)); - if (subAckPacket == null) throw new ArgumentNullException(nameof(subAckPacket)); - + if (subscribePacket == null) + { + throw new ArgumentNullException(nameof(subscribePacket)); + } + + if (subAckPacket == null) + { + throw new ArgumentNullException(nameof(subAckPacket)); + } + // MQTTv5.0.0 handling. if (subAckPacket.ReasonCodes.Any() && subAckPacket.ReasonCodes.Count != subscribePacket.TopicFilters.Count) { - throw new MqttProtocolViolationException( - "The reason codes are not matching the topic filters [MQTT-3.9.3-1]."); + throw new MqttProtocolViolationException("The reason codes are not matching the topic filters [MQTT-3.9.3-1]."); } - + var items = new List(); for (var i = 0; i < subscribePacket.TopicFilters.Count; i++) { items.Add(CreateSubscribeResultItem(i, subscribePacket, subAckPacket)); } - - var result = new MqttClientSubscribeResult - { - PacketIdentifier = subAckPacket.PacketIdentifier, - ReasonString = subAckPacket.ReasonString, - UserProperties = subAckPacket.UserProperties ?? EmptyUserProperties, - Items = items - }; - - return result; + + return new MqttClientSubscribeResult(subAckPacket.PacketIdentifier, items, subAckPacket.ReasonString, subAckPacket.UserProperties ?? EmptyUserProperties); } static MqttClientSubscribeResultItem CreateSubscribeResultItem(int index, MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket) { - var resultCode = (MqttClientSubscribeResultCode) subAckPacket.ReasonCodes[index]; - - return new MqttClientSubscribeResultItem - { - TopicFilter = subscribePacket.TopicFilters[index], - ResultCode = resultCode, - }; + var resultCode = (MqttClientSubscribeResultCode)subAckPacket.ReasonCodes[index]; + return new MqttClientSubscribeResultItem(subscribePacket.TopicFilters[index], resultCode); } } } \ No newline at end of file diff --git a/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResultItem.cs b/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResultItem.cs index f1fcee8797aa8f156a0e2a4aad9d002e49852854..620ccffed3ab6338f8530976a6116c099bd4734c 100644 --- a/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResultItem.cs +++ b/Source/MQTTnet/Client/Subscribing/MqttClientSubscribeResultItem.cs @@ -2,22 +2,29 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using MQTTnet.Packets; namespace MQTTnet.Client { public sealed class MqttClientSubscribeResultItem { + public MqttClientSubscribeResultItem(MqttTopicFilter topicFilter, MqttClientSubscribeResultCode resultCode) + { + TopicFilter = topicFilter ?? throw new ArgumentNullException(nameof(topicFilter)); + ResultCode = resultCode; + } + /// /// Gets or sets the topic filter. /// The topic filter can contain topics and wildcards. /// - public MqttTopicFilter TopicFilter { get; internal set; } + public MqttTopicFilter TopicFilter { get; } /// /// Gets or sets the result code. - /// Hint: MQTT 5 feature only. + /// MQTT 5.0.0+ feature. /// - public MqttClientSubscribeResultCode ResultCode { get; internal set; } + public MqttClientSubscribeResultCode ResultCode { get; } } } diff --git a/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResult.cs b/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResult.cs index 6bd69401013f228fe7de571959067b5409eda380..24b8008f006549331f04075fa8b899b81616a47c 100644 --- a/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResult.cs +++ b/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResult.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using MQTTnet.Packets; @@ -9,23 +10,38 @@ namespace MQTTnet.Client { public sealed class MqttClientUnsubscribeResult { - public IReadOnlyCollection Items { get; internal set; } + public MqttClientUnsubscribeResult( + ushort packetIdentifier, + IReadOnlyCollection items, + string reasonString, + IReadOnlyCollection userProperties) + { + PacketIdentifier = packetIdentifier; + Items = items ?? throw new ArgumentNullException(nameof(items)); + ReasonString = reasonString; + UserProperties = userProperties ?? throw new ArgumentNullException(nameof(userProperties)); + } /// - /// Gets the user properties which were part of the UNSUBACK packet. - /// MQTTv5 only. + /// Gets the result for every topic filter item. /// - public IReadOnlyCollection UserProperties { get; internal set; } - + public IReadOnlyCollection Items { get; } + /// - /// Gets the reason string. - /// MQTTv5 only. + /// Gets the packet identifier which was used. /// - public string ReasonString { get; internal set; } - + public ushort PacketIdentifier { get; } + + /// + /// Gets the reason string. + /// MQTT 5.0.0+ feature. + /// + public string ReasonString { get; } + /// - /// Gets the packet identifier which was used. + /// Gets the user properties which were part of the UNSUBACK packet. + /// MQTT 5.0.0+ feature. /// - public ushort PacketIdentifier { get; internal set; } + public IReadOnlyCollection UserProperties { get; } } -} +} \ No newline at end of file diff --git a/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResultFactory.cs b/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResultFactory.cs index 0f872958ff4904a2b7062b42ac5b6416227c2356..d204fd94e5092a3304f6ca0527e87d62cb1dd26b 100644 --- a/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResultFactory.cs +++ b/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResultFactory.cs @@ -12,17 +12,23 @@ namespace MQTTnet.Client public sealed class MqttClientUnsubscribeResultFactory { static readonly IReadOnlyCollection EmptyUserProperties = new List(); - + public MqttClientUnsubscribeResult Create(MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket) { - if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket)); - if (unsubAckPacket == null) throw new ArgumentNullException(nameof(unsubAckPacket)); + if (unsubscribePacket == null) + { + throw new ArgumentNullException(nameof(unsubscribePacket)); + } + + if (unsubAckPacket == null) + { + throw new ArgumentNullException(nameof(unsubAckPacket)); + } // MQTTv3.1.1 has no reason code at all! if (unsubAckPacket.ReasonCodes != null && unsubAckPacket.ReasonCodes.Count != unsubscribePacket.TopicFilters.Count) { - throw new MqttProtocolViolationException( - "The return codes are not matching the topic filters [MQTT-3.9.3-1]."); + throw new MqttProtocolViolationException("The return codes are not matching the topic filters [MQTT-3.9.3-1]."); } var items = new List(); @@ -30,33 +36,21 @@ namespace MQTTnet.Client { items.Add(CreateUnsubscribeResultItem(i, unsubscribePacket, unsubAckPacket)); } - - var result = new MqttClientUnsubscribeResult - { - PacketIdentifier = unsubAckPacket.PacketIdentifier, - ReasonString = unsubAckPacket.ReasonString, - UserProperties = unsubAckPacket.UserProperties ?? EmptyUserProperties, - Items = items - }; - - return result; + + return new MqttClientUnsubscribeResult(unsubscribePacket.PacketIdentifier, items, unsubAckPacket.ReasonString, unsubAckPacket.UserProperties ?? EmptyUserProperties); } - + static MqttClientUnsubscribeResultItem CreateUnsubscribeResultItem(int index, MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket) { var resultCode = MqttClientUnsubscribeResultCode.Success; - + if (unsubAckPacket.ReasonCodes != null) { // MQTTv3.1.1 has no reason code and no return code!. - resultCode = (MqttClientUnsubscribeResultCode) unsubAckPacket.ReasonCodes[index]; + resultCode = (MqttClientUnsubscribeResultCode)unsubAckPacket.ReasonCodes[index]; } - - return new MqttClientUnsubscribeResultItem - { - TopicFilter = unsubscribePacket.TopicFilters[index], - ResultCode = resultCode - }; + + return new MqttClientUnsubscribeResultItem(unsubscribePacket.TopicFilters[index], resultCode); } } } \ No newline at end of file diff --git a/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResultItem.cs b/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResultItem.cs index 9f39035703e264fa7b5e550f164993e6d35edae4..997a2493abe4553030d76c29994141f211791deb 100644 --- a/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResultItem.cs +++ b/Source/MQTTnet/Client/Unsubscribing/MqttClientUnsubscribeResultItem.cs @@ -2,20 +2,28 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; + namespace MQTTnet.Client { public sealed class MqttClientUnsubscribeResultItem { + public MqttClientUnsubscribeResultItem(string topicFilter, MqttClientUnsubscribeResultCode resultCode) + { + TopicFilter = topicFilter ?? throw new ArgumentNullException(nameof(topicFilter)); + ResultCode = resultCode; + } + /// - /// Gets or sets the topic filter. - /// The topic filter can contain topics and wildcards. + /// Gets or sets the result code. + /// MQTT 5.0.0+ feature. /// - public string TopicFilter { get; internal set; } - + public MqttClientUnsubscribeResultCode ResultCode { get; } + /// - /// Gets or sets the result code. - /// Hint: MQTT 5 feature only. + /// Gets or sets the topic filter. + /// The topic filter can contain topics and wildcards. /// - public MqttClientUnsubscribeResultCode ResultCode { get; internal set; } + public string TopicFilter { get; } } } \ No newline at end of file diff --git a/Source/MQTTnet/Diagnostics/PacketInspection/InspectMqttPacketEventArgs.cs b/Source/MQTTnet/Diagnostics/PacketInspection/InspectMqttPacketEventArgs.cs index 6d9e3a67410e6df7f9360f7aea77994d1bdc772c..549fe863f226068bf355c090a6b356c76af6e240 100644 --- a/Source/MQTTnet/Diagnostics/PacketInspection/InspectMqttPacketEventArgs.cs +++ b/Source/MQTTnet/Diagnostics/PacketInspection/InspectMqttPacketEventArgs.cs @@ -8,8 +8,14 @@ namespace MQTTnet.Diagnostics { public sealed class InspectMqttPacketEventArgs : EventArgs { - public MqttPacketFlowDirection Direction { get; internal set; } + public InspectMqttPacketEventArgs(MqttPacketFlowDirection direction, byte[] buffer) + { + Direction = direction; + Buffer = buffer ?? throw new ArgumentNullException(nameof(buffer)); + } - public byte[] Buffer { get; set; } + public byte[] Buffer { get; } + + public MqttPacketFlowDirection Direction { get; } } } \ No newline at end of file diff --git a/Source/MQTTnet/Server/Events/PreparingSessionEventArgs.cs b/Source/MQTTnet/Server/Events/PreparingSessionEventArgs.cs index e635308a37e2971706c2f4b206ee19e821cd1e2c..584c4d4ec46d4fea2bff26e65ef488ac2446786d 100644 --- a/Source/MQTTnet/Server/Events/PreparingSessionEventArgs.cs +++ b/Source/MQTTnet/Server/Events/PreparingSessionEventArgs.cs @@ -10,7 +10,7 @@ namespace MQTTnet.Server { public sealed class PreparingSessionEventArgs : EventArgs { - public string Id { get; internal set; } + public string Id { get; set; } // TODO: Allow adding of packets to the queue etc. diff --git a/Source/MQTTnet/Server/Events/ValidatingConnectionEventArgs.cs b/Source/MQTTnet/Server/Events/ValidatingConnectionEventArgs.cs index 35b70369f53e7213f71526faff2e74694380bfc5..096b2da126f71984e9a6765faa54339889c13288 100644 --- a/Source/MQTTnet/Server/Events/ValidatingConnectionEventArgs.cs +++ b/Source/MQTTnet/Server/Events/ValidatingConnectionEventArgs.cs @@ -9,7 +9,6 @@ using System.Security.Cryptography.X509Certificates; using System.Text; using MQTTnet.Adapter; using MQTTnet.Formatter; -using MQTTnet.Implementations; using MQTTnet.Internal; using MQTTnet.Packets; using MQTTnet.Protocol; @@ -20,10 +19,11 @@ namespace MQTTnet.Server { readonly MqttConnectPacket _connectPacket; - public ValidatingConnectionEventArgs(MqttConnectPacket connectPacket, IMqttChannelAdapter clientAdapter) + public ValidatingConnectionEventArgs(MqttConnectPacket connectPacket, IMqttChannelAdapter clientAdapter, IDictionary sessionItems) { _connectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket)); ChannelAdapter = clientAdapter ?? throw new ArgumentNullException(nameof(clientAdapter)); + SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems)); } /// @@ -34,13 +34,13 @@ namespace MQTTnet.Server /// /// Gets or sets the authentication data. - /// Hint: MQTT 5 feature only. + /// MQTT 5.0.0+ feature. /// public byte[] AuthenticationData => _connectPacket.AuthenticationData; /// /// Gets or sets the authentication method. - /// Hint: MQTT 5 feature only. + /// MQTT 5.0.0+ feature. /// public string AuthenticationMethod => _connectPacket.AuthenticationMethod; @@ -99,7 +99,7 @@ namespace MQTTnet.Server /// Gets or sets the reason code. When a MQTTv3 client connects the enum value must be one which is /// also supported in MQTTv3. Otherwise the connection attempt will fail because not all codes can be /// converted properly. - /// MQTTv5 only. + /// MQTT 5.0.0+ feature. /// public MqttConnectReasonCode ReasonCode { get; set; } = MqttConnectReasonCode.Success; @@ -114,19 +114,19 @@ namespace MQTTnet.Server /// /// Gets the request problem information. - /// Hint: MQTT 5 feature only. + /// MQTT 5.0.0+ feature. /// public bool RequestProblemInformation => _connectPacket.RequestProblemInformation; /// /// Gets the request response information. - /// Hint: MQTT 5 feature only. + /// MQTT 5.0.0+ feature. /// public bool RequestResponseInformation => _connectPacket.RequestResponseInformation; /// /// Gets or sets the response authentication data. - /// MQTTv5 only. + /// MQTT 5.0.0+ feature. /// public byte[] ResponseAuthenticationData { get; set; } @@ -137,14 +137,14 @@ namespace MQTTnet.Server /// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add /// metadata to MQTT messages and pass information between publisher, broker, and subscriber. /// The feature is very similar to the HTTP header concept. - /// Hint: MQTT 5 feature only. + /// MQTT 5.0.0+ feature. /// public List ResponseUserProperties { get; set; } /// /// Gets or sets the server reference. This can be used together with i.e. "Server Moved" to send /// a different server address to the client. - /// MQTTv5 only. + /// MQTT 5.0.0+ feature. /// public string ServerReference { get; set; } @@ -158,7 +158,7 @@ namespace MQTTnet.Server /// /// Gets or sets a key/value collection that can be used to share data within the scope of this session. /// - public IDictionary SessionItems { get; internal set; } + public IDictionary SessionItems { get; } /// /// Gets or sets the topic alias maximum. @@ -169,7 +169,7 @@ namespace MQTTnet.Server [Obsolete("This property name has a typo. Use 'UserName' instead. This one will be removed soon.")] public string Username => _connectPacket.Username; - + public string UserName => _connectPacket.Username; /// @@ -179,7 +179,7 @@ namespace MQTTnet.Server /// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add /// metadata to MQTT messages and pass information between publisher, broker, and subscriber. /// The feature is very similar to the HTTP header concept. - /// Hint: MQTT 5 feature only. + /// MQTT 5.0.0+ feature. /// public List UserProperties => _connectPacket.UserProperties; diff --git a/Source/MQTTnet/Server/InjectedMqttApplicationMessage.cs b/Source/MQTTnet/Server/InjectedMqttApplicationMessage.cs index 7d027ab6ebe8a5b5ebf49b20cf72a82a5f26bbf7..ed829b2390d5fd8972ccb24ff43de6d845cc8586 100644 --- a/Source/MQTTnet/Server/InjectedMqttApplicationMessage.cs +++ b/Source/MQTTnet/Server/InjectedMqttApplicationMessage.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections; namespace MQTTnet.Server { @@ -13,8 +14,15 @@ namespace MQTTnet.Server ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage)); } - public string SenderClientId { get; set; } = string.Empty; + public MqttApplicationMessage ApplicationMessage { get; } + + /// + /// Gets or sets the session items which should be used for all event handlers which are involved in message + /// processing. + /// If _null_ is specified the singleton session items from the server are used instead. + /// + public IDictionary CustomSessionItems { get; set; } - public MqttApplicationMessage ApplicationMessage { get; set; } + public string SenderClientId { get; set; } = string.Empty; } } \ No newline at end of file diff --git a/Source/MQTTnet/Server/Internal/MqttClientSessionsManager.cs b/Source/MQTTnet/Server/Internal/MqttClientSessionsManager.cs index b76380ab885462de0ce3aef19cbe6f3f9d82ecc9..e3dee096e6e43b47dbdb24e8513b6c52cf4a5f60 100644 --- a/Source/MQTTnet/Server/Internal/MqttClientSessionsManager.cs +++ b/Source/MQTTnet/Server/Internal/MqttClientSessionsManager.cs @@ -669,11 +669,9 @@ namespace MQTTnet.Server async Task ValidateConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter) { - var eventArgs = new ValidatingConnectionEventArgs(connectPacket, channelAdapter) - { - SessionItems = new ConcurrentDictionary() - }; - + // TODO: Load session items from persisted sessions in the future. + var sessionItems = new ConcurrentDictionary(); + var eventArgs = new ValidatingConnectionEventArgs(connectPacket, channelAdapter, sessionItems); await _eventContainer.ValidatingConnectionEvent.InvokeAsync(eventArgs).ConfigureAwait(false); // Check the client ID and set a random one if supported. diff --git a/Source/MQTTnet/Server/MqttServer.cs b/Source/MQTTnet/Server/MqttServer.cs index 6568f0b56d88efa1285de20dc4096f960519f0ad..d848261408096e04fcbcb5f9b1e0d866a07c467c 100644 --- a/Source/MQTTnet/Server/MqttServer.cs +++ b/Source/MQTTnet/Server/MqttServer.cs @@ -28,8 +28,6 @@ namespace MQTTnet.Server readonly MqttRetainedMessagesManager _retainedMessagesManager; readonly IMqttNetLogger _rootLogger; - readonly IDictionary _sessionItems = new ConcurrentDictionary(); - CancellationTokenSource _cancellationTokenSource; public MqttServer(MqttServerOptions options, IEnumerable adapters, IMqttNetLogger logger) @@ -167,6 +165,12 @@ namespace MQTTnet.Server public bool IsStarted => _cancellationTokenSource != null; + /// + /// Gives access to the session items which belong to this server. This session items are passed + /// to several events instead of the client session items if the event is caused by the server instead of a client. + /// + public IDictionary ServerSessionItems { get; } = new ConcurrentDictionary(); + public Task DeleteRetainedMessagesAsync() { ThrowIfNotStarted(); @@ -228,9 +232,11 @@ namespace MQTTnet.Server throw new NotSupportedException("Injected application messages must contain a topic. Topic alias is not supported."); } + var sessionItems = injectedApplicationMessage.CustomSessionItems ?? ServerSessionItems; + return _clientSessionsManager.DispatchApplicationMessage( injectedApplicationMessage.SenderClientId, - _sessionItems, + sessionItems, injectedApplicationMessage.ApplicationMessage, cancellationToken); }