mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-15 00:15:15 +08:00
Improve performance and drop support for old frameworks (#2068)
* Remove WebSocket4Net extension library * Remove UWP support * Drop support for all old frameworks * Update CI * Update CI * Update CI * Update CI * Remove obsolete code * Set default protocol version to 5.0.0 * Remove obsolete tests * Remove old dependencies * Reset release notes * Move server to dedicated project * Fix unit tests * Update and cleanup nuget packages * Fix unit tests Refactor code * No longer throw exception when receiving non success CONNACK * Enable nuget signing * Enable nuget signing * Enable nuget signing * Enable nuget signing * Enable nuget signing * Enable nuget signing * Enable nuget signing * Enable nuget signing * Enable code signing * Enable code signing * Enable code signing * Fix client endpoint handling * Improve CI * Fix CI * Fix CI * Fix CI * Fix CI * Import changes from version 4 * Cleanup target frameworks and remove obsolete code * Drop support for dotnet7.0 * Remove managed client * Version5 auditmode (#2016) Added NuGet audit mode and some other settings to get informed about security issues. * Address warnings from analyzers * Support for ReadOnlySequence as payload (#2046) * use ReadOnlySequence as payload * fix build * fix build * skip sign in forks * remove unnecessary owner * tests and extensions * remove test * simplify WritePacketBuffer, remove unused helper * Adjust namespaces * Fix build errors * Import latest fixes from main branch * Adjust namespaces --------- Co-authored-by: SeppPenner <HansM2013@gmx.de> Co-authored-by: Martin Regen <mregen@microsoft.com>
This commit is contained in:
co-authored by
SeppPenner
Martin Regen
parent
e18a91a4b5
commit
980a5d0a6d
@@ -0,0 +1,50 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.AspNetCore;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Tests.ASP.Mockups;
|
||||
|
||||
public sealed class ConnectionHandlerMockup : IMqttServerAdapter
|
||||
{
|
||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
public TaskCompletionSource<MqttConnectionContext> Context { get; } = new();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public async Task OnConnectedAsync(ConnectionContext connection)
|
||||
{
|
||||
try
|
||||
{
|
||||
var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535));
|
||||
var context = new MqttConnectionContext(formatter, connection);
|
||||
Context.TrySetResult(context);
|
||||
|
||||
await ClientHandler(context);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Context.TrySetException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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.
|
||||
|
||||
using System.IO.Pipelines;
|
||||
|
||||
namespace MQTTnet.Tests.ASP.Mockups
|
||||
{
|
||||
public sealed class DuplexPipeMockup : IDuplexPipe
|
||||
{
|
||||
public DuplexPipeMockup()
|
||||
{
|
||||
var pool = new LimitedMemoryPool();
|
||||
var pipeOptions = new PipeOptions(pool);
|
||||
Receive = new Pipe(pipeOptions);
|
||||
Send = new Pipe(pipeOptions);
|
||||
}
|
||||
|
||||
PipeReader IDuplexPipe.Input => Receive.Reader;
|
||||
|
||||
PipeWriter IDuplexPipe.Output => Send.Writer;
|
||||
|
||||
public Pipe Receive { get; }
|
||||
|
||||
public Pipe Send { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// 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.
|
||||
|
||||
using System.Buffers;
|
||||
|
||||
namespace MQTTnet.Tests.ASP.Mockups
|
||||
{
|
||||
public sealed class LimitedMemoryPool : MemoryPool<byte>
|
||||
{
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
}
|
||||
|
||||
public override IMemoryOwner<byte> Rent(int minBufferSize = -1)
|
||||
{
|
||||
return new MemoryOwner(minBufferSize);
|
||||
}
|
||||
|
||||
public override int MaxBufferSize { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
|
||||
namespace MQTTnet.Tests.ASP.Mockups
|
||||
{
|
||||
public sealed class MemoryOwner : IMemoryOwner<byte>
|
||||
{
|
||||
readonly byte[] _raw;
|
||||
|
||||
public MemoryOwner(int size)
|
||||
{
|
||||
if (size <= 0)
|
||||
{
|
||||
size = 1024;
|
||||
}
|
||||
|
||||
if (size > 4096)
|
||||
{
|
||||
size = 4096;
|
||||
}
|
||||
|
||||
_raw = ArrayPool<byte>.Shared.Rent(size);
|
||||
Memory = _raw;
|
||||
}
|
||||
|
||||
public Memory<byte> Memory { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(_raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.AspNetCore;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Tests.ASP.Mockups;
|
||||
using MQTTnet.Tests.Helpers;
|
||||
|
||||
namespace MQTTnet.Tests.ASP
|
||||
{
|
||||
[TestClass]
|
||||
public class MqttConnectionContextTest
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task TestCorruptedConnectPacket()
|
||||
{
|
||||
var writer = new MqttBufferWriter(4096, 65535);
|
||||
var serializer = new MqttPacketFormatterAdapter(writer);
|
||||
var pipe = new DuplexPipeMockup();
|
||||
var connection = new DefaultConnectionContext();
|
||||
connection.Transport = pipe;
|
||||
var ctx = new MqttConnectionContext(serializer, connection);
|
||||
|
||||
await pipe.Receive.Writer.WriteAsync(writer.AddMqttHeader(MqttControlPacketType.Connect, Array.Empty<byte>()));
|
||||
|
||||
await Assert.ThrowsExceptionAsync<MqttProtocolViolationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
|
||||
|
||||
// the first exception should complete the pipes so if someone tries to use the connection after that it should throw immidiatly
|
||||
await Assert.ThrowsExceptionAsync<InvalidOperationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
|
||||
}
|
||||
|
||||
// TODO: Fix test
|
||||
// [TestMethod]
|
||||
// public async Task TestEndpoint()
|
||||
// {
|
||||
// var mockup = new ConnectionHandlerMockup();
|
||||
//
|
||||
// using (var host = new WebHostBuilder().UseKestrel(kestrel => kestrel.ListenLocalhost(1883, listener => listener.Use((ctx, next) => mockup.OnConnectedAsync(ctx))))
|
||||
// .UseStartup<Startup>()
|
||||
// .ConfigureServices(
|
||||
// (hostContext, services) =>
|
||||
// {
|
||||
// services.AddHostedMqttServer(o => o.WithoutDefaultEndpoint());
|
||||
// services.AddSingleton<IMqttServerAdapter>(mockup);
|
||||
// })
|
||||
// .Build())
|
||||
//
|
||||
// using (var client = new MqttFactory().CreateMqttClient())
|
||||
// {
|
||||
// host.Start();
|
||||
// await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build(), CancellationToken.None);
|
||||
//
|
||||
// var ctx = await mockup.Context.Task;
|
||||
//
|
||||
// var ep = IPEndPoint.Parse(ctx.Endpoint);
|
||||
// Assert.IsNotNull(ep);
|
||||
//
|
||||
// Assert.IsNotNull(ctx);
|
||||
// }
|
||||
// }
|
||||
|
||||
// COMMENTED OUT DUE TO DEAD LOCK? OR VERY VERY SLOW PERFORMANCE ON LOCAL DEV MACHINE. TEST WAS STILL RUNNING AFTER SEVERAL MINUTES!
|
||||
//[TestMethod]
|
||||
//public async Task TestParallelWrites()
|
||||
//{
|
||||
// var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311);
|
||||
// var pipe = new DuplexPipeMockup();
|
||||
// var connection = new DefaultConnectionContext();
|
||||
// connection.Transport = pipe;
|
||||
// var ctx = new MqttConnectionContext(serializer, connection);
|
||||
|
||||
// var tasks = Enumerable.Range(1, 100).Select(_ => Task.Run(async () =>
|
||||
// {
|
||||
// for (int i = 0; i < 100; i++)
|
||||
// {
|
||||
// await ctx.SendPacketAsync(new MqttPublishPacket(), TimeSpan.Zero, CancellationToken.None).ConfigureAwait(false);
|
||||
// }
|
||||
// }));
|
||||
|
||||
// await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
//}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestLargePacket()
|
||||
{
|
||||
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
|
||||
var pipe = new DuplexPipeMockup();
|
||||
var connection = new DefaultConnectionContext();
|
||||
connection.Transport = pipe;
|
||||
var ctx = new MqttConnectionContext(serializer, connection);
|
||||
|
||||
await ctx.SendPacketAsync(new MqttPublishPacket { PayloadSegment = new byte[20_000] }, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
var readResult = await pipe.Send.Reader.ReadAsync();
|
||||
Assert.IsTrue(readResult.Buffer.Length > 20000);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestReceivePacketAsyncThrowsWhenReaderCompleted()
|
||||
{
|
||||
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
|
||||
var pipe = new DuplexPipeMockup();
|
||||
var connection = new DefaultConnectionContext();
|
||||
connection.Transport = pipe;
|
||||
var ctx = new MqttConnectionContext(serializer, connection);
|
||||
|
||||
pipe.Receive.Writer.Complete();
|
||||
|
||||
await Assert.ThrowsExceptionAsync<MqttCommunicationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
|
||||
}
|
||||
|
||||
class Startup
|
||||
{
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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.
|
||||
|
||||
using System.Buffers;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.AspNetCore;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Tests.ASP;
|
||||
|
||||
[TestClass]
|
||||
public sealed class ReaderExtensionsTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestTryDeserialize()
|
||||
{
|
||||
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
|
||||
|
||||
var buffer = serializer.Encode(new MqttPublishPacket { Topic = "a", PayloadSegment = new byte[5] }).Join();
|
||||
|
||||
var sequence = new ReadOnlySequence<byte>(buffer.Array, buffer.Offset, buffer.Count);
|
||||
|
||||
var part = sequence;
|
||||
var consumed = part.Start;
|
||||
var observed = part.Start;
|
||||
var read = 0;
|
||||
|
||||
part = sequence.Slice(sequence.Start, 0); // empty message should fail
|
||||
var result = serializer.TryDecode(part, out _, out consumed, out observed, out read);
|
||||
Assert.IsFalse(result);
|
||||
|
||||
part = sequence.Slice(sequence.Start, 1); // partial fixed header should fail
|
||||
result = serializer.TryDecode(part, out _, out consumed, out observed, out read);
|
||||
Assert.IsFalse(result);
|
||||
|
||||
part = sequence.Slice(sequence.Start, 4); // partial body should fail
|
||||
result = serializer.TryDecode(part, out _, out consumed, out observed, out read);
|
||||
Assert.IsFalse(result);
|
||||
|
||||
part = sequence; // complete msg should work
|
||||
result = serializer.TryDecode(part, out _, out consumed, out observed, out read);
|
||||
Assert.IsTrue(result);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user