diff --git a/Build/MQTTnet.AspNetCore.nuspec b/Build/MQTTnet.AspNetCore.nuspec index cb1718f3..8809fac6 100644 --- a/Build/MQTTnet.AspNetCore.nuspec +++ b/Build/MQTTnet.AspNetCore.nuspec @@ -10,7 +10,8 @@ https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png false This is a support library to integrate MQTTnet into AspNetCore. - * For more release notes please check the MQTTnet release notes. + * For more release notes please check the MQTTnet release notes. + * [Server] fixed cpu spike in case a client disconnectes (issue 421) Copyright Christian Kratky 2016-2018 MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin diff --git a/Source/MQTTnet.AspnetCore/MqttConnectionContext.cs b/Source/MQTTnet.AspnetCore/MqttConnectionContext.cs index 8a8469de..361eb6a1 100644 --- a/Source/MQTTnet.AspnetCore/MqttConnectionContext.cs +++ b/Source/MQTTnet.AspnetCore/MqttConnectionContext.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Connections; using MQTTnet.Adapter; using MQTTnet.AspNetCore.Client.Tcp; +using MQTTnet.Exceptions; using MQTTnet.Packets; using MQTTnet.Serializer; using System; @@ -83,7 +84,7 @@ namespace MQTTnet.AspNetCore } else if (readResult.IsCompleted) { - break; + throw new MqttCommunicationException("Connection Aborted"); } } finally diff --git a/Source/MQTTnet/Implementations/MqttTcpServerListener.cs b/Source/MQTTnet/Implementations/MqttTcpServerListener.cs index e3522979..e72c831d 100644 --- a/Source/MQTTnet/Implementations/MqttTcpServerListener.cs +++ b/Source/MQTTnet/Implementations/MqttTcpServerListener.cs @@ -20,8 +20,8 @@ namespace MQTTnet.Implementations private readonly CancellationToken _cancellationToken; private readonly AddressFamily _addressFamily; private readonly MqttServerTcpEndpointBaseOptions _options; + private readonly MqttServerTlsTcpEndpointOptions _tlsOptions; private readonly X509Certificate2 _tlsCertificate; - private Socket _socket; public MqttTcpServerListener( @@ -36,6 +36,11 @@ namespace MQTTnet.Implementations _tlsCertificate = tlsCertificate; _cancellationToken = cancellationToken; _logger = logger.CreateChildLogger(nameof(MqttTcpServerListener)); + + if (_options is MqttServerTlsTcpEndpointOptions tlsOptions) + { + _tlsOptions = tlsOptions; + } } public event EventHandler ClientAccepted; @@ -75,7 +80,7 @@ namespace MQTTnet.Implementations if (_tlsCertificate != null) { sslStream = new SslStream(new NetworkStream(clientSocket), false); - await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, SslProtocols.Tls12, false).ConfigureAwait(false); + await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, _tlsOptions.SslProtocol, false).ConfigureAwait(false); } _logger.Verbose("Client '{0}' accepted by TCP listener '{1}, {2}'.", diff --git a/Source/MQTTnet/Server/MqttServerOptionsBuilder.cs b/Source/MQTTnet/Server/MqttServerOptionsBuilder.cs index f54f379f..45400620 100644 --- a/Source/MQTTnet/Server/MqttServerOptionsBuilder.cs +++ b/Source/MQTTnet/Server/MqttServerOptionsBuilder.cs @@ -1,5 +1,6 @@ using System; using System.Net; +using System.Security.Authentication; namespace MQTTnet.Server { @@ -80,6 +81,12 @@ namespace MQTTnet.Server return this; } + public MqttServerOptionsBuilder WithEncryptionSslProtocol(SslProtocols value) + { + _options.TlsEndpointOptions.SslProtocol = value; + return this; + } + public MqttServerOptionsBuilder WithoutEncryptedEndpoint() { _options.TlsEndpointOptions.IsEnabled = false; diff --git a/Source/MQTTnet/Server/MqttServerTlsTcpEndpointOptions.cs b/Source/MQTTnet/Server/MqttServerTlsTcpEndpointOptions.cs index 76d6c862..88d2f86d 100644 --- a/Source/MQTTnet/Server/MqttServerTlsTcpEndpointOptions.cs +++ b/Source/MQTTnet/Server/MqttServerTlsTcpEndpointOptions.cs @@ -1,4 +1,6 @@ -namespace MQTTnet.Server +using System.Security.Authentication; + +namespace MQTTnet.Server { public class MqttServerTlsTcpEndpointOptions : MqttServerTcpEndpointBaseOptions { @@ -8,5 +10,8 @@ } public byte[] Certificate { get; set; } + + + public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12; } } diff --git a/Tests/MQTTnet.AspNetCore.Tests/MQTTnet.AspNetCore.Tests.csproj b/Tests/MQTTnet.AspNetCore.Tests/MQTTnet.AspNetCore.Tests.csproj index 55f60d91..998acd2f 100644 --- a/Tests/MQTTnet.AspNetCore.Tests/MQTTnet.AspNetCore.Tests.csproj +++ b/Tests/MQTTnet.AspNetCore.Tests/MQTTnet.AspNetCore.Tests.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1 diff --git a/Tests/MQTTnet.AspNetCore.Tests/Mockups/ConnectionContextMockup.cs b/Tests/MQTTnet.AspNetCore.Tests/Mockups/ConnectionContextMockup.cs new file mode 100644 index 00000000..296959ef --- /dev/null +++ b/Tests/MQTTnet.AspNetCore.Tests/Mockups/ConnectionContextMockup.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.IO.Pipelines; +using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Http.Features; + +namespace MQTTnet.AspNetCore.Tests.Mockups +{ + public class ConnectionContextMockup : ConnectionContext + { + public override string ConnectionId { get; set; } + + public override IFeatureCollection Features { get; } + + public override IDictionary Items { get; set; } + public override IDuplexPipe Transport { get; set; } + + public ConnectionContextMockup() + { + //Transport = new DefaultConnectionContext + } + } +} diff --git a/Tests/MQTTnet.AspNetCore.Tests/Mockups/DuplexPipeMockup.cs b/Tests/MQTTnet.AspNetCore.Tests/Mockups/DuplexPipeMockup.cs new file mode 100644 index 00000000..1774f180 --- /dev/null +++ b/Tests/MQTTnet.AspNetCore.Tests/Mockups/DuplexPipeMockup.cs @@ -0,0 +1,16 @@ +using System.IO.Pipelines; + +namespace MQTTnet.AspNetCore.Tests.Mockups +{ + public class DuplexPipeMockup : IDuplexPipe + { + PipeReader IDuplexPipe.Input => Receive.Reader; + + PipeWriter IDuplexPipe.Output => Send.Writer; + + + public Pipe Receive { get; set; } = new Pipe(); + + public Pipe Send { get; set; } = new Pipe(); + } +} diff --git a/Tests/MQTTnet.AspNetCore.Tests/MqttConnectionContextTest.cs b/Tests/MQTTnet.AspNetCore.Tests/MqttConnectionContextTest.cs new file mode 100644 index 00000000..7498015e --- /dev/null +++ b/Tests/MQTTnet.AspNetCore.Tests/MqttConnectionContextTest.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore.Connections; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MQTTnet.AspNetCore.Tests.Mockups; +using MQTTnet.Exceptions; +using MQTTnet.Serializer; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace MQTTnet.AspNetCore.Tests +{ + [TestClass] + public class MqttConnectionContextTest + { + [TestMethod] + public async Task TestReceivePacketAsyncThrowsWhenReaderCompleted() + { + var serializer = new MqttPacketSerializer {}; + var pipe = new DuplexPipeMockup(); + var connection = new DefaultConnectionContext(); + connection.Transport = pipe; + var ctx = new MqttConnectionContext(serializer, connection); + + pipe.Receive.Writer.Complete(); + + await Assert.ThrowsExceptionAsync(() => ctx.ReceivePacketAsync(TimeSpan.FromSeconds(1), CancellationToken.None)); + } + } +}