From 0ef9c8825e37f072f892c63eaa71c2f418f717e9 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 12 Feb 2022 09:52:17 +0100 Subject: [PATCH] Removed ${aspnet-request-posted-body} because NLog 5.0 only supports threadsafe layoutrenderers (#754) --- .../ASP.NET 4.6.1 - VS2017/NLog.config | 2 +- .../ASP.NET Core 2 - VS2017/NLog.config | 4 +- .../ASP.NET Core 3 - VS2019/NLog.config | 4 +- .../ASP.NET Core 3 - VS2019/Startup.cs | 8 - .../ASP.NET Core 5 NLog Example/nlog.config | 4 +- .../ASP.NET Core 6 NLog Example/nlog.config | 4 +- .../AspNetRequestPostedBody.cs | 183 ------------------ .../AspNetRequestPostedBodyTests.cs | 119 ------------ 8 files changed, 9 insertions(+), 319 deletions(-) delete mode 100644 src/Shared/LayoutRenderers/AspNetRequestPostedBody.cs delete mode 100644 tests/Shared/LayoutRenderers/AspNetRequestPostedBodyTests.cs diff --git a/examples/ASP.NET 4.6.1/Visual Studio 2017/ASP.NET 4.6.1 - VS2017/NLog.config b/examples/ASP.NET 4.6.1/Visual Studio 2017/ASP.NET 4.6.1 - VS2017/NLog.config index ee6ee08a..2fa6d38f 100644 --- a/examples/ASP.NET 4.6.1/Visual Studio 2017/ASP.NET 4.6.1 - VS2017/NLog.config +++ b/examples/ASP.NET 4.6.1/Visual Studio 2017/ASP.NET 4.6.1 - VS2017/NLog.config @@ -15,7 +15,7 @@ + layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}|${callsite}" /> diff --git a/examples/ASP.NET Core 2/Visual Studio 2017/ASP.NET Core 2 - VS2017/NLog.config b/examples/ASP.NET Core 2/Visual Studio 2017/ASP.NET Core 2 - VS2017/NLog.config index f42d2553..36b7c768 100644 --- a/examples/ASP.NET Core 2/Visual Studio 2017/ASP.NET Core 2 - VS2017/NLog.config +++ b/examples/ASP.NET Core 2/Visual Studio 2017/ASP.NET Core 2 - VS2017/NLog.config @@ -15,11 +15,11 @@ + layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}" /> + layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" /> diff --git a/examples/ASP.NET Core 3/ASP.NET Core 3 - VS2019/NLog.config b/examples/ASP.NET Core 3/ASP.NET Core 3 - VS2019/NLog.config index 0a62c3a0..951aa05a 100644 --- a/examples/ASP.NET Core 3/ASP.NET Core 3 - VS2019/NLog.config +++ b/examples/ASP.NET Core 3/ASP.NET Core 3 - VS2019/NLog.config @@ -15,11 +15,11 @@ + layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}" /> + layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" /> diff --git a/examples/ASP.NET Core 3/ASP.NET Core 3 - VS2019/Startup.cs b/examples/ASP.NET Core 3/ASP.NET Core 3 - VS2019/Startup.cs index ead12a89..ed110394 100644 --- a/examples/ASP.NET Core 3/ASP.NET Core 3 - VS2019/Startup.cs +++ b/examples/ASP.NET Core 3/ASP.NET Core 3 - VS2019/Startup.cs @@ -45,20 +45,12 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseRouting(); app.UseAuthorization(); - // needed for ${aspnet-request-posted-body} with an API Controller. Must be before app.UseEndpoints - app.Use(async (context, next) => { - context.Request.EnableBuffering(); - await next(); - }); - app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); - - } } } diff --git a/examples/ASP.NET Core 5/ASP.NET Core 5 NLog Example/nlog.config b/examples/ASP.NET Core 5/ASP.NET Core 5 NLog Example/nlog.config index 0b7abe44..8e34f4a1 100644 --- a/examples/ASP.NET Core 5/ASP.NET Core 5 NLog Example/nlog.config +++ b/examples/ASP.NET Core 5/ASP.NET Core 5 NLog Example/nlog.config @@ -15,11 +15,11 @@ + layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}" /> + layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}|${callsite}" /> diff --git a/examples/ASP.NET Core 6/ASP.NET Core 6 NLog Example/nlog.config b/examples/ASP.NET Core 6/ASP.NET Core 6 NLog Example/nlog.config index 19935c25..7131ddd7 100644 --- a/examples/ASP.NET Core 6/ASP.NET Core 6 NLog Example/nlog.config +++ b/examples/ASP.NET Core 6/ASP.NET Core 6 NLog Example/nlog.config @@ -15,11 +15,11 @@ + layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}" /> + layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}|${callsite}" /> diff --git a/src/Shared/LayoutRenderers/AspNetRequestPostedBody.cs b/src/Shared/LayoutRenderers/AspNetRequestPostedBody.cs deleted file mode 100644 index 54748ad8..00000000 --- a/src/Shared/LayoutRenderers/AspNetRequestPostedBody.cs +++ /dev/null @@ -1,183 +0,0 @@ -using System; -using System.IO; -using System.Text; -using System.Threading; -using NLog.Common; -using NLog.Config; -using NLog.LayoutRenderers; -using NLog.Web.Internal; - -#if ASP_NET_CORE -using System.Threading.Tasks; -using HttpRequest = Microsoft.AspNetCore.Http.HttpRequest; -#else -using System.Web; -using HttpRequest = System.Web.HttpRequestBase; -#endif - -namespace NLog.Web.LayoutRenderers -{ - /// - /// ASP.NET posted body, e.g. FORM or Ajax POST - /// - /// - /// Example usage of ${aspnet-request-posted-body}: - /// - /// ${aspnet-request-posted-body} - Produces - {username:xyz,password:xyz} - /// - /// - [LayoutRenderer("aspnet-request-posted-body")] - public class AspNetRequestPostedBody : AspNetLayoutRendererBase - { - private const int Size64KiloBytes = 64 * 1024; - private const int Size30Kilobytes = 30 * 1024; - - /// - /// Max size in bytes of the body. Skip logging of the body when larger. - /// Default 30720 Bytes = 30 KiB - /// (0 = No limit, -1 = No Buffer Limit) - /// - public int MaxContentLength { get; set; } = Size30Kilobytes; - - /// - /// Renders the ASP.NET posted body - /// - /// The to append the rendered data to. - /// Logging event. - protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent) - { - var httpRequest = HttpContextAccessor.HttpContext.TryGetRequest(); - if (httpRequest == null) - { - return; - } - - long? contentLength = httpRequest.ContentLength; - if (!TryGetBody(httpRequest, contentLength, out var body)) - { - return; // No Body to read - } - - var content = BodyToString(body); - builder.Append(content); - } - - private static string BodyToString(Stream body) - { - var oldPosition = body.Position; - body.Position = 0; - try - { - // Note: don't dispose the StreamReader, it will close the stream and that's unwanted. You could pass that that - // to the StreamReader in some platforms, but then the dispose will be a NOOP, so for platform compat just don't dispose - var bodyReader = new StreamReader(body); - -#if ASP_NET_CORE - var content = bodyReader.ReadToEndAsync().ConfigureAwait(false).GetAwaiter().GetResult(); -#else - var content = bodyReader.ReadToEnd(); -#endif - return content; - } - finally - { - //restore - body.Position = oldPosition; - } - } - - private bool TryGetBody(HttpRequest httpRequest, long? contentLength, out Stream body) - { - body = null; - if (contentLength <= 0) - { - return false; - } - - if (MaxContentLength > 0 && contentLength > MaxContentLength) - { - InternalLogger.Debug("AspNetRequestPostedBody: body stream is too big. ContentLength={0}", contentLength); - return false; - } - - body = GetBodyStream(httpRequest); - - if (body == null) - { - InternalLogger.Debug("AspNetRequestPostedBody: body stream was null"); - return false; - } - - if (!body.CanRead) - { - InternalLogger.Debug("AspNetRequestPostedBody: body stream has been closed"); - return false; - } - - if (!body.CanSeek) - { - if (!TryEnableBuffering(httpRequest, contentLength, out body)) - return false; - } - else - { - if (MaxContentLength > 0 && !contentLength.HasValue && body.Length > MaxContentLength) - { - InternalLogger.Debug("AspNetRequestPostedBody: body stream too big. Body.Length={0}", body.Length); - body = null; - return false; - } - } - - return true; - } - - private static Stream GetBodyStream(HttpRequest httpRequest) - { -#if ASP_NET_CORE - var body = httpRequest.Body; -#else - var body = httpRequest.InputStream; -#endif - return body; - } - - ///Can seek now? - private bool TryEnableBuffering(HttpRequest httpRequest, long? contentLength, out Stream bodyStream) - { - bodyStream = null; - - if (MaxContentLength >= 0 && !contentLength.HasValue) - { - InternalLogger.Debug("AspNetRequestPostedBody: body stream cannot seek with unknown ContentLength"); - return false; - } - - int bufferThreshold = MaxContentLength <= 0 ? Size64KiloBytes : MaxContentLength; - if (MaxContentLength == 0 && contentLength > bufferThreshold) - { - InternalLogger.Debug("AspNetRequestPostedBody: body stream cannot seek and stream is too big. ContentLength={0}", contentLength); - return false; - } - - bodyStream = EnableRewind(httpRequest, bufferThreshold); - if (bodyStream?.CanSeek != true) - { - InternalLogger.Debug("AspNetRequestPostedBody: body stream cannot seek"); - return false; - } - - return true; - } - - private static Stream EnableRewind(HttpRequest httpRequest, int bufferThreshold) - { -#if ASP_NET_CORE - Microsoft.AspNetCore.Http.HttpRequestRewindExtensions.EnableBuffering(httpRequest, bufferThreshold); - return httpRequest.Body; -#else - return null; -#endif - } - } -} diff --git a/tests/Shared/LayoutRenderers/AspNetRequestPostedBodyTests.cs b/tests/Shared/LayoutRenderers/AspNetRequestPostedBodyTests.cs deleted file mode 100644 index b096a446..00000000 --- a/tests/Shared/LayoutRenderers/AspNetRequestPostedBodyTests.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -#if ASP_NET_CORE -using Microsoft.AspNetCore.Http; -using HttpContextBase = Microsoft.AspNetCore.Http.HttpContext; -#else -using System.Web; -#endif -using NLog.Web.LayoutRenderers; -using NLog.Web.Tests.LayoutRenderers; -using NSubstitute; -using Xunit; - -namespace NLog.Web.Tests.LayoutRenderers -{ - public class AspNetRequestPostedBodyTests : LayoutRenderersTestBase - { - [Fact] - public void NullStreamRendersEmptyString() - { - // Arrange - var (renderer, httpContext) = CreateWithHttpContext(); - SetBodyStream(httpContext, null); - - var logEventInfo = new LogEventInfo(); - - // Act - string result = renderer.Render(logEventInfo); - - // Assert - Assert.Empty(result); - } - - [Theory] - [InlineData(0)] - [InlineData(10)] - public void CorrectStreamRendersFullStreamAndRestorePosition(long position) - { - // Arrange - var (renderer, httpContext) = CreateWithHttpContext(); - - var json = "{user: 'foo', password: '123'}"; - var stream = CreateStream(json); - stream.Position = position; - SetBodyStream(httpContext, stream); - - var logEventInfo = new LogEventInfo(); - - // Act - string result = renderer.Render(logEventInfo); - - // Assert - Assert.Equal(json, result); - Assert.Equal(position, stream.Position); - } - - [Theory] - [InlineData("", null, "")] - [InlineData("", 0, "")] - [InlineData("ABCDEFGHIJK", null, "ABCDEFGHIJK")] - [InlineData("ABCDEFGHIJK", 0, "ABCDEFGHIJK")] - [InlineData("ABCDEFGHIJK", 50, "ABCDEFGHIJK")] - [InlineData("ABCDEFGHIJK", 10, "")] - public void MaxContentLengthProtectsAgainstLargeBodyStreamTest(string body, int? maxContentLength, string expectedResult) - { - MaxContentLengthProtectsAgainstLargeBodyStream(body, maxContentLength, expectedResult, false); -#if ASP_NET_CORE - MaxContentLengthProtectsAgainstLargeBodyStream(body, maxContentLength, expectedResult, true); -#endif - } - - private static void MaxContentLengthProtectsAgainstLargeBodyStream(string body, int? maxContentLength, string expectedResult, bool canReadOnce) - { - // Arrange - var (renderer, httpContext) = CreateWithHttpContext(); - if (maxContentLength.HasValue) - renderer.MaxContentLength = maxContentLength.Value; - - var stream = CreateStream(body, canReadOnce); - SetBodyStream(httpContext, stream); - - var logEventInfo = new LogEventInfo(); - - // Act - string result = renderer.Render(logEventInfo); - - // Assert - Assert.Equal(expectedResult, result); - } - - private static void SetBodyStream(HttpContextBase httpContext, Stream stream) - { -#if ASP_NET_CORE - httpContext.Request.Body.Returns(stream); -#else - httpContext.Request.InputStream.Returns(stream); -#endif - httpContext.Request.ContentLength.Returns((int)(stream?.Length ?? 0)); - } - - private static MemoryStream CreateStream(string content, bool readOnce = false) - { - var stream = readOnce ? new ReadOnceStream() : new MemoryStream(); - var streamWriter = new StreamWriter(stream); - streamWriter.Write(content); - streamWriter.Flush(); - if (readOnce) - stream.Position = 0; - return stream; - } - - class ReadOnceStream : MemoryStream - { - public override bool CanSeek => false; - } - } -}