From d50cb023d70b3263f713ba790010d6d84a56d643 Mon Sep 17 00:00:00 2001 From: Adam Radocz Date: Sat, 6 Nov 2021 21:10:11 +0100 Subject: [PATCH 1/4] Update NuGet packages. --- .../ClientApplication/ClientApplication.csproj | 6 +++--- .../ServerApplication/ServerApplication.csproj | 4 ++-- .../Bedrock.Framework.Experimental.csproj | 8 ++++++-- src/Bedrock.Framework/Bedrock.Framework.csproj | 8 ++++++-- .../Bedrock.Framework.Benchmarks.csproj | 4 ++-- .../Bedrock.Framework.Tests.csproj | 16 +++++++++++----- 6 files changed, 30 insertions(+), 16 deletions(-) diff --git a/samples/ClientApplication/ClientApplication.csproj b/samples/ClientApplication/ClientApplication.csproj index 07519626..3a7c1b5f 100644 --- a/samples/ClientApplication/ClientApplication.csproj +++ b/samples/ClientApplication/ClientApplication.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net6.0 @@ -14,8 +14,8 @@ - - + + diff --git a/samples/ServerApplication/ServerApplication.csproj b/samples/ServerApplication/ServerApplication.csproj index bfb90c4d..06d83219 100644 --- a/samples/ServerApplication/ServerApplication.csproj +++ b/samples/ServerApplication/ServerApplication.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net6.0 @@ -10,7 +10,7 @@ - + diff --git a/src/Bedrock.Framework.Experimental/Bedrock.Framework.Experimental.csproj b/src/Bedrock.Framework.Experimental/Bedrock.Framework.Experimental.csproj index 7bba7647..9f600cdd 100644 --- a/src/Bedrock.Framework.Experimental/Bedrock.Framework.Experimental.csproj +++ b/src/Bedrock.Framework.Experimental/Bedrock.Framework.Experimental.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net6.0 true Experimental protocols and transports for Bedrock.Framework. David Fowler @@ -18,6 +18,10 @@ - + + + + + diff --git a/src/Bedrock.Framework/Bedrock.Framework.csproj b/src/Bedrock.Framework/Bedrock.Framework.csproj index 861ce1e6..0b402352 100644 --- a/src/Bedrock.Framework/Bedrock.Framework.csproj +++ b/src/Bedrock.Framework/Bedrock.Framework.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net6.0 High performance, low level networking APIs for building custom severs and clients. David Fowler true @@ -12,6 +12,10 @@ - + + + + + diff --git a/tests/Bedrock.Framework.Benchmarks/Bedrock.Framework.Benchmarks.csproj b/tests/Bedrock.Framework.Benchmarks/Bedrock.Framework.Benchmarks.csproj index 3782ba29..5fb3d2e1 100644 --- a/tests/Bedrock.Framework.Benchmarks/Bedrock.Framework.Benchmarks.csproj +++ b/tests/Bedrock.Framework.Benchmarks/Bedrock.Framework.Benchmarks.csproj @@ -2,11 +2,11 @@ Exe - netcoreapp3.1 + net6.0 - + diff --git a/tests/Bedrock.Framework.Tests/Bedrock.Framework.Tests.csproj b/tests/Bedrock.Framework.Tests/Bedrock.Framework.Tests.csproj index e678b9a9..1b94f27a 100644 --- a/tests/Bedrock.Framework.Tests/Bedrock.Framework.Tests.csproj +++ b/tests/Bedrock.Framework.Tests/Bedrock.Framework.Tests.csproj @@ -1,16 +1,22 @@  - netcoreapp3.1 + net6.0 false - - - - + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + From 8f38cec6df062725b8770885fba07b14e85ba5ce Mon Sep 17 00:00:00 2001 From: Adam Radocz Date: Sat, 6 Nov 2021 21:49:58 +0100 Subject: [PATCH 2/4] Fix to build in .NET 6.0 framework. --- samples/ServerApplication/MqttApplication.cs | 7 +++---- .../Protocols/Http1RequestMessageReader.cs | 2 +- .../Protocols/Http1ResponseMessageReader.cs | 2 +- tests/Bedrock.Framework.Tests/ProtocolTests.cs | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/samples/ServerApplication/MqttApplication.cs b/samples/ServerApplication/MqttApplication.cs index 3b964821..3b74d663 100644 --- a/samples/ServerApplication/MqttApplication.cs +++ b/samples/ServerApplication/MqttApplication.cs @@ -19,7 +19,7 @@ private async Task OnClientConnectedAsync(IMqttChannelAdapter adapter) { while (true) { - var packet = await adapter.ReceivePacketAsync(Timeout.InfiniteTimeSpan, default); + var packet = await adapter.ReceivePacketAsync(default); switch (packet) { @@ -29,8 +29,7 @@ await adapter.SendPacketAsync(new MqttConnAckPacket ReturnCode = MqttConnectReturnCode.ConnectionAccepted, ReasonCode = MqttConnectReasonCode.Success, IsSessionPresent = false - }, Timeout.InfiniteTimeSpan, - default); + }, default); break; case MqttDisconnectPacket disconnectPacket: break; @@ -48,7 +47,7 @@ await adapter.SendPacketAsync(new MqttConnAckPacket }; ack.ReasonCodes.Add(MqttSubscribeReasonCode.GrantedQoS0); - await adapter.SendPacketAsync(ack, Timeout.InfiniteTimeSpan, default); + await adapter.SendPacketAsync(ack, default); break; default: break; diff --git a/src/Bedrock.Framework.Experimental/Protocols/Http1RequestMessageReader.cs b/src/Bedrock.Framework.Experimental/Protocols/Http1RequestMessageReader.cs index 1718d212..9245a139 100644 --- a/src/Bedrock.Framework.Experimental/Protocols/Http1RequestMessageReader.cs +++ b/src/Bedrock.Framework.Experimental/Protocols/Http1RequestMessageReader.cs @@ -54,7 +54,7 @@ public bool TryParseMessage(in ReadOnlySequence input, ref SequencePositio goto case State.Headers; case State.Headers: - while (sequenceReader.TryReadTo(out var headerLine, NewLine)) + while (sequenceReader.TryReadTo(out ReadOnlySequence headerLine, NewLine)) { if (headerLine.Length == 0) { diff --git a/src/Bedrock.Framework.Experimental/Protocols/Http1ResponseMessageReader.cs b/src/Bedrock.Framework.Experimental/Protocols/Http1ResponseMessageReader.cs index 02cc8f0d..d0f44633 100644 --- a/src/Bedrock.Framework.Experimental/Protocols/Http1ResponseMessageReader.cs +++ b/src/Bedrock.Framework.Experimental/Protocols/Http1ResponseMessageReader.cs @@ -59,7 +59,7 @@ public bool TryParseMessage(in ReadOnlySequence input, ref SequencePositio goto case State.Headers; case State.Headers: - while (sequenceReader.TryReadTo(out var headerLine, NewLine)) + while (sequenceReader.TryReadTo(out ReadOnlySequence headerLine, NewLine)) { if (headerLine.Length == 0) { diff --git a/tests/Bedrock.Framework.Tests/ProtocolTests.cs b/tests/Bedrock.Framework.Tests/ProtocolTests.cs index b748d83f..5446b5cb 100644 --- a/tests/Bedrock.Framework.Tests/ProtocolTests.cs +++ b/tests/Bedrock.Framework.Tests/ProtocolTests.cs @@ -274,7 +274,7 @@ public async Task ReadAfterCancellationTokenFiresWorks() var cts = new CancellationTokenSource(); cts.Cancel(); - await Assert.ThrowsAsync(async () => await reader.ReadAsync(protocol, cts.Token)); + await Assert.ThrowsAsync(async () => await reader.ReadAsync(protocol, cts.Token)); await connection.Application.Output.WriteAsync(data); From 3ce5dd71db3617a68c5b024dbfb8bb669f7f7fa3 Mon Sep 17 00:00:00 2001 From: Adam Radocz Date: Mon, 8 Nov 2021 20:43:39 +0100 Subject: [PATCH 3/4] Update NuGet packages. --- samples/ClientApplication/ClientApplication.csproj | 2 +- .../Bedrock.Framework.Experimental.csproj | 2 +- src/Bedrock.Framework/Bedrock.Framework.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/ClientApplication/ClientApplication.csproj b/samples/ClientApplication/ClientApplication.csproj index 3a7c1b5f..44a199db 100644 --- a/samples/ClientApplication/ClientApplication.csproj +++ b/samples/ClientApplication/ClientApplication.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/Bedrock.Framework.Experimental/Bedrock.Framework.Experimental.csproj b/src/Bedrock.Framework.Experimental/Bedrock.Framework.Experimental.csproj index 9f600cdd..64cda6ec 100644 --- a/src/Bedrock.Framework.Experimental/Bedrock.Framework.Experimental.csproj +++ b/src/Bedrock.Framework.Experimental/Bedrock.Framework.Experimental.csproj @@ -18,7 +18,7 @@ - + diff --git a/src/Bedrock.Framework/Bedrock.Framework.csproj b/src/Bedrock.Framework/Bedrock.Framework.csproj index 0b402352..9d1f2358 100644 --- a/src/Bedrock.Framework/Bedrock.Framework.csproj +++ b/src/Bedrock.Framework/Bedrock.Framework.csproj @@ -12,7 +12,7 @@ - + From f3e1e99bcbd732a7a264d5c950949da63f6d33c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Rad=C3=B3cz?= Date: Fri, 17 Dec 2021 20:22:13 +0100 Subject: [PATCH 4/4] Optimize the Logging. - Add StringBuilder Pool. - Add "in" keyword. - Use char insted of a string when appending to the StringBuilder. - Convert methods to use expression body. --- samples/ServerApplication/Program.cs | 23 +++- .../Middleware/ConnectionBuilderExtensions.cs | 12 +- .../Middleware/Internal/LoggingStream.cs | 105 ++++++------------ .../Middleware/LoggingConnectionMiddleware.cs | 12 +- 4 files changed, 66 insertions(+), 86 deletions(-) diff --git a/samples/ServerApplication/Program.cs b/samples/ServerApplication/Program.cs index 359c2bed..37cb557b 100644 --- a/samples/ServerApplication/Program.cs +++ b/samples/ServerApplication/Program.cs @@ -1,12 +1,15 @@ using System; using System.Net; using System.Security.Cryptography.X509Certificates; +using System.Text; using System.Threading.Tasks; using Bedrock.Framework; using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.ObjectPool; namespace ServerApplication { @@ -24,26 +27,36 @@ public static async Task Main(string[] args) services.AddSignalR(); + services.TryAddSingleton(); + services.TryAddSingleton>(serviceProvider => + { + var objectPoolProvider = serviceProvider.GetRequiredService(); + var policy = new StringBuilderPooledObjectPolicy(); + return objectPoolProvider.Create(policy); + }); + var serviceProvider = services.BuildServiceProvider(); var server = new ServerBuilder(serviceProvider) .UseSockets(sockets => { + var stringBuilderPool = serviceProvider.GetRequiredService>(); + // Echo server sockets.ListenLocalhost(5000, - builder => builder.UseConnectionLogging().UseConnectionHandler()); + builder => builder.UseConnectionLogging(stringBuilderPool: stringBuilderPool).UseConnectionHandler()); // HTTP/1.1 server sockets.Listen(IPAddress.Loopback, 5001, - builder => builder.UseConnectionLogging().UseConnectionHandler()); + builder => builder.UseConnectionLogging(stringBuilderPool: stringBuilderPool).UseConnectionHandler()); // SignalR Hub sockets.Listen(IPAddress.Loopback, 5002, - builder => builder.UseConnectionLogging().UseHub()); + builder => builder.UseConnectionLogging(stringBuilderPool: stringBuilderPool).UseHub()); // MQTT application sockets.Listen(IPAddress.Loopback, 5003, - builder => builder.UseConnectionLogging().UseConnectionHandler()); + builder => builder.UseConnectionLogging(stringBuilderPool: stringBuilderPool).UseConnectionHandler()); // Echo Server with TLS sockets.Listen(IPAddress.Loopback, 5004, @@ -54,7 +67,7 @@ public static async Task Main(string[] args) // NOTE: Do not do this in a production environment options.AllowAnyRemoteCertificate(); }) - .UseConnectionLogging().UseConnectionHandler()); + .UseConnectionLogging(stringBuilderPool: stringBuilderPool).UseConnectionHandler()); sockets.Listen(IPAddress.Loopback, 5005, builder => builder.UseConnectionLogging().UseConnectionHandler()); diff --git a/src/Bedrock.Framework/Middleware/ConnectionBuilderExtensions.cs b/src/Bedrock.Framework/Middleware/ConnectionBuilderExtensions.cs index e9b95a9e..9a91633a 100644 --- a/src/Bedrock.Framework/Middleware/ConnectionBuilderExtensions.cs +++ b/src/Bedrock.Framework/Middleware/ConnectionBuilderExtensions.cs @@ -1,12 +1,11 @@ using System; -using System.Collections.Generic; using System.Text; -using Bedrock.Framework.Infrastructure; using Bedrock.Framework.Middleware.Tls; using Microsoft.AspNetCore.Connections; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.ObjectPool; namespace Bedrock.Framework { @@ -17,11 +16,16 @@ public static class ConnectionBuilderExtensions /// /// Emits verbose logs for bytes read from and written to the connection. /// - public static TBuilder UseConnectionLogging(this TBuilder builder, string loggerName = null, ILoggerFactory loggerFactory = null, LoggingFormatter loggingFormatter = null) where TBuilder : IConnectionBuilder + public static TBuilder UseConnectionLogging(this TBuilder builder, string loggerName = null, ILoggerFactory loggerFactory = null, ObjectPool stringBuilderPool = null, LoggingFormatter loggingFormatter = null) where TBuilder : IConnectionBuilder { loggerFactory ??= builder.ApplicationServices.GetRequiredService(); var logger = loggerName == null ? loggerFactory.CreateLogger() : loggerFactory.CreateLogger(loggerName); - builder.Use(next => new LoggingConnectionMiddleware(next, logger, loggingFormatter).OnConnectionAsync); + if (stringBuilderPool == null) + { + var objectPoolProvider = new DefaultObjectPoolProvider(); + stringBuilderPool = objectPoolProvider.CreateStringBuilderPool(); + } + builder.Use(next => new LoggingConnectionMiddleware(next, logger, stringBuilderPool, loggingFormatter).OnConnectionAsync); return builder; } diff --git a/src/Bedrock.Framework/Middleware/Internal/LoggingStream.cs b/src/Bedrock.Framework/Middleware/Internal/LoggingStream.cs index c750eca9..7d25befa 100644 --- a/src/Bedrock.Framework/Middleware/Internal/LoggingStream.cs +++ b/src/Bedrock.Framework/Middleware/Internal/LoggingStream.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.ObjectPool; namespace Bedrock.Framework.Infrastructure { @@ -14,69 +15,34 @@ internal sealed class LoggingStream : Stream { private readonly Stream _inner; private readonly ILogger _logger; + private readonly ObjectPool _stringBuilderPool; private readonly LoggingFormatter _logFormatter; - public LoggingStream(Stream inner, ILogger logger, LoggingFormatter logFormatter = null) + public LoggingStream(Stream inner, ILogger logger, ObjectPool stringBuilderPool, LoggingFormatter logFormatter = null) { _inner = inner; _logger = logger; + _stringBuilderPool = stringBuilderPool; _logFormatter = logFormatter; } - public override bool CanRead - { - get - { - return _inner.CanRead; - } - } + public override bool CanRead => _inner.CanRead; - public override bool CanSeek - { - get - { - return _inner.CanSeek; - } - } + public override bool CanSeek => _inner.CanSeek; - public override bool CanWrite - { - get - { - return _inner.CanWrite; - } - } + public override bool CanWrite => _inner.CanWrite; - public override long Length - { - get - { - return _inner.Length; - } - } + public override long Length => _inner.Length; public override long Position { - get - { - return _inner.Position; - } - - set - { - _inner.Position = value; - } + get => _inner.Position; + set => _inner.Position = value; } - public override void Flush() - { - _inner.Flush(); - } + public override void Flush() => _inner.Flush(); - public override Task FlushAsync(CancellationToken cancellationToken) - { - return _inner.FlushAsync(cancellationToken); - } + public override Task FlushAsync(CancellationToken cancellationToken) => _inner.FlushAsync(cancellationToken); public override int Read(byte[] buffer, int offset, int count) { @@ -140,7 +106,7 @@ public override ValueTask WriteAsync(ReadOnlyMemory source, CancellationTo return _inner.WriteAsync(source, cancellationToken); } - private void Log(string method, ReadOnlySpan buffer) + private void Log(in string method, in ReadOnlySpan buffer) { if (_logFormatter != null) { @@ -153,20 +119,20 @@ private void Log(string method, ReadOnlySpan buffer) return; } - var builder = new StringBuilder(); + var builder = _stringBuilderPool.Get(); builder.AppendLine($"{method}[{buffer.Length}]"); - var charBuilder = new StringBuilder(); + var charBuilder = _stringBuilderPool.Get(); // Write the hex for (int i = 0; i < buffer.Length; i++) { builder.Append(buffer[i].ToString("X2")); - builder.Append(" "); + builder.Append(' '); var bufferChar = (char)buffer[i]; if (char.IsControl(bufferChar)) { - charBuilder.Append("."); + charBuilder.Append('.'); } else { @@ -175,15 +141,15 @@ private void Log(string method, ReadOnlySpan buffer) if ((i + 1) % 16 == 0) { - builder.Append(" "); - builder.Append(charBuilder.ToString()); + builder.Append(' ', 2); + builder.Append(charBuilder); builder.AppendLine(); charBuilder.Clear(); } else if ((i + 1) % 8 == 0) { - builder.Append(" "); - charBuilder.Append(" "); + builder.Append(' '); + charBuilder.Append(' '); } } @@ -193,32 +159,25 @@ private void Log(string method, ReadOnlySpan buffer) builder.Append(string.Empty.PadRight(2 + (3 * (16 - charBuilder.Length)))); // extra for space after 8th byte if (charBuilder.Length < 8) - builder.Append(" "); - builder.Append(charBuilder.ToString()); + { + builder.Append(' '); + } + builder.Append(charBuilder); } _logger.LogDebug(builder.ToString()); + + _stringBuilderPool.Return(builder); + _stringBuilderPool.Return(charBuilder); } // The below APM methods call the underlying Read/WriteAsync methods which will still be logged. - public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) - { - return TaskToApm.Begin(ReadAsync(buffer, offset, count), callback, state); - } + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => TaskToApm.Begin(ReadAsync(buffer, offset, count), callback, state); - public override int EndRead(IAsyncResult asyncResult) - { - return TaskToApm.End(asyncResult); - } + public override int EndRead(IAsyncResult asyncResult) => TaskToApm.End(asyncResult); - public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) - { - return TaskToApm.Begin(WriteAsync(buffer, offset, count), callback, state); - } + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => TaskToApm.Begin(WriteAsync(buffer, offset, count), callback, state); - public override void EndWrite(IAsyncResult asyncResult) - { - TaskToApm.End(asyncResult); - } + public override void EndWrite(IAsyncResult asyncResult) => TaskToApm.End(asyncResult); } } diff --git a/src/Bedrock.Framework/Middleware/LoggingConnectionMiddleware.cs b/src/Bedrock.Framework/Middleware/LoggingConnectionMiddleware.cs index 00c4108a..3ef1c351 100644 --- a/src/Bedrock.Framework/Middleware/LoggingConnectionMiddleware.cs +++ b/src/Bedrock.Framework/Middleware/LoggingConnectionMiddleware.cs @@ -3,10 +3,12 @@ using System; using System.IO.Pipelines; +using System.Text; using System.Threading.Tasks; using Bedrock.Framework.Infrastructure; using Microsoft.AspNetCore.Connections; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.ObjectPool; namespace Bedrock.Framework { @@ -15,11 +17,13 @@ internal class LoggingConnectionMiddleware private readonly ConnectionDelegate _next; private readonly ILogger _logger; private readonly LoggingFormatter _loggingFormatter; + private readonly ObjectPool _stringBuilderPool; - public LoggingConnectionMiddleware(ConnectionDelegate next, ILogger logger, LoggingFormatter loggingFormatter = null) + public LoggingConnectionMiddleware(ConnectionDelegate next, ILogger logger, ObjectPool stringBuilderPool, LoggingFormatter loggingFormatter = null) { _next = next ?? throw new ArgumentNullException(nameof(next)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _stringBuilderPool = stringBuilderPool ?? throw new ArgumentNullException(nameof(stringBuilderPool)); _loggingFormatter = loggingFormatter; } @@ -29,7 +33,7 @@ public async Task OnConnectionAsync(ConnectionContext context) try { - await using (var loggingDuplexPipe = new LoggingDuplexPipe(context.Transport, _logger, _loggingFormatter)) + await using (var loggingDuplexPipe = new LoggingDuplexPipe(context.Transport, _logger, _stringBuilderPool, _loggingFormatter)) { context.Transport = loggingDuplexPipe; @@ -44,8 +48,8 @@ public async Task OnConnectionAsync(ConnectionContext context) private class LoggingDuplexPipe : DuplexPipeStreamAdapter { - public LoggingDuplexPipe(IDuplexPipe transport, ILogger logger, LoggingFormatter loggingFormatter) : - base(transport, stream => new LoggingStream(stream, logger, loggingFormatter)) + public LoggingDuplexPipe(IDuplexPipe transport, ILogger logger, ObjectPool stringBuilderPool, LoggingFormatter loggingFormatter) : + base(transport, stream => new LoggingStream(stream, logger, stringBuilderPool, loggingFormatter)) { } }