From 13518c0a4ad213dca7709388e71eac8bd6ea7a1f Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 30 Jun 2023 08:50:03 +0200 Subject: [PATCH] NLogRequestPostedBodyMiddleware - Fixed handling of Stream.CanSeek --- .../NLogRequestPostedBodyMiddleware.cs | 12 +++- .../NLogRequestPostedBodyMiddlewareTests.cs | 61 ++++++++++++++++--- 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/src/NLog.Web.AspNetCore/NLogRequestPostedBodyMiddleware.cs b/src/NLog.Web.AspNetCore/NLogRequestPostedBodyMiddleware.cs index 1b6972da..e8eaf5d6 100644 --- a/src/NLog.Web.AspNetCore/NLogRequestPostedBodyMiddleware.cs +++ b/src/NLog.Web.AspNetCore/NLogRequestPostedBodyMiddleware.cs @@ -97,10 +97,9 @@ private bool ShouldCaptureRequestBody(HttpContext context) return false; } - // If we cannot seek the stream we cannot capture the body - if (!postedBody.CanSeek) + if (postedBody.Position != 0 && !postedBody.CanSeek) { - InternalLogger.Debug("NLogRequestPostedBodyMiddleware: HttpContext.Request.Body stream is non-seekable"); + InternalLogger.Debug("NLogRequestPostedBodyMiddleware: HttpContext.Request.Body stream position non-zero"); return false; } @@ -116,6 +115,13 @@ private bool ShouldCaptureRequestBody(HttpContext context) /// The contents of the Stream read fully from start to end as a String private static async Task GetString(Stream stream) { + // If we cannot seek the stream we cannot capture the body + if (!stream.CanSeek) + { + InternalLogger.Debug("NLogRequestPostedBodyMiddleware: HttpContext.Request.Body stream is non-seekable"); + return string.Empty; + } + string responseText = null; // Save away the original stream position diff --git a/tests/NLog.Web.AspNetCore.Tests/NLogRequestPostedBodyMiddlewareTests.cs b/tests/NLog.Web.AspNetCore.Tests/NLogRequestPostedBodyMiddlewareTests.cs index 57ce7fa8..1473066f 100644 --- a/tests/NLog.Web.AspNetCore.Tests/NLogRequestPostedBodyMiddlewareTests.cs +++ b/tests/NLog.Web.AspNetCore.Tests/NLogRequestPostedBodyMiddlewareTests.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; @@ -65,7 +66,6 @@ public void EmptyBodyTest() // Assert Assert.NotNull(defaultContext.Items); Assert.Empty(defaultContext.Items); - Assert.Null(defaultContext.Items[AspNetRequestPostedBodyLayoutRenderer.NLogPostedRequestBodyKey]); } [Fact] @@ -86,7 +86,6 @@ public void ExcludContentTypeTest() // Assert Assert.NotNull(defaultContext.Items); Assert.Empty(defaultContext.Items); - Assert.Null(defaultContext.Items[AspNetRequestPostedBodyLayoutRenderer.NLogPostedRequestBodyKey]); } [Fact] @@ -203,21 +202,67 @@ public void CannotSeekLengthTest() // Arrange DefaultHttpContext defaultContext = new DefaultHttpContext(); - defaultContext.Request.Body = Substitute.For(); - defaultContext.Request.ContentLength = 1; + defaultContext.Request.Body = new NetworkStream() { _length = 2 }; + defaultContext.Request.ContentLength = 2; defaultContext.Request.ContentType = "text/plain"; - defaultContext.Request.Body.CanRead.Returns(true); - defaultContext.Request.Body.CanSeek.Returns(false); - // Act var middlewareInstance = new NLogRequestPostedBodyMiddleware(Next,NLogRequestPostedBodyMiddlewareOptions.Default); middlewareInstance.Invoke(defaultContext).ConfigureAwait(false).GetAwaiter().GetResult(); + // Assert + Assert.NotNull(defaultContext.Items); + Assert.Single(defaultContext.Items); + Assert.NotNull(defaultContext.Items[AspNetRequestPostedBodyLayoutRenderer.NLogPostedRequestBodyKey]); + Assert.True(defaultContext.Items[AspNetRequestPostedBodyLayoutRenderer.NLogPostedRequestBodyKey] is string); + } + + [Fact] + public void CannotSeekLengthAndStartedTest() + { + // Arrange + DefaultHttpContext defaultContext = new DefaultHttpContext(); + + defaultContext.Request.Body = new NetworkStream() { _length = 2, Position = 2 }; + defaultContext.Request.ContentLength = 2; + defaultContext.Request.ContentType = "text/plain"; + + // Act + var middlewareInstance = + new NLogRequestPostedBodyMiddleware(Next, NLogRequestPostedBodyMiddlewareOptions.Default); + middlewareInstance.Invoke(defaultContext).ConfigureAwait(false).GetAwaiter().GetResult(); + // Assert Assert.NotNull(defaultContext.Items); Assert.Empty(defaultContext.Items); } + + private sealed class NetworkStream : Stream + { + internal long _length; + + public override bool CanRead => true; + + public override bool CanSeek => false; + + public override bool CanWrite => false; + + public override long Length => _length; + + public override long Position { get; set; } + + public override int Read(byte[] buffer, int offset, int count) + { + int delta = Math.Min((int)(Length - Position), count); + Position += delta; + return delta; + } + + public override long Seek(long offset, SeekOrigin origin) => throw new System.NotSupportedException(); + public override void SetLength(long value) => throw new System.NotSupportedException(); + public override void Write(byte[] buffer, int offset, int count) => throw new System.NotSupportedException(); + public override void Flush() => throw new System.NotSupportedException(); + } } }