diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.ReadWrite.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.ReadWrite.cs index ad1cfdb979..89266d9e9a 100644 --- a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.ReadWrite.cs +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.ReadWrite.cs @@ -15,11 +15,25 @@ namespace Microsoft.AspNetCore.Server.IISIntegration /// /// Reads data from the Input pipe to the user. /// - /// + /// + /// + /// /// /// - internal async Task ReadAsync(Memory memory, CancellationToken cancellationToken) + public async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { + // Start a task which will continuously call ReadFromIISAsync and WriteToIISAsync + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + if (count == 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + var memory = new Memory(buffer, offset, count); + StartProcessingRequestAndResponseBody(); while (true) @@ -30,7 +44,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration { if (!readableBuffer.IsEmpty) { - var actual = Math.Min(readableBuffer.Length, memory.Length); + var actual = Math.Min(readableBuffer.Length, count); readableBuffer = readableBuffer.Slice(0, actual); readableBuffer.CopyTo(memory.Span); return (int)actual; @@ -53,7 +67,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration /// /// /// - internal Task WriteAsync(ReadOnlyMemory memory, CancellationToken cancellationToken = default(CancellationToken)) + public Task WriteAsync(ReadOnlyMemory memory, CancellationToken cancellationToken = default(CancellationToken)) { // Want to keep exceptions consistent, @@ -74,7 +88,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration /// /// /// - internal Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken)) + public Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken)) { if (!_hasResponseStarted) { diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpRequestBody.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpRequestBody.cs index be5746c0d3..0a51d9fc7a 100644 --- a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpRequestBody.cs +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpRequestBody.cs @@ -39,9 +39,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration public override unsafe Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { - var memory = new Memory(buffer, offset, count); - - return _httpContext.ReadAsync(memory, cancellationToken); + return _httpContext.ReadAsync(buffer, offset, count, cancellationToken); } public override long Seek(long offset, SeekOrigin origin) diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpResponseBody.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpResponseBody.cs index f850994a4a..38c8e64c2a 100644 --- a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpResponseBody.cs +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpResponseBody.cs @@ -54,6 +54,11 @@ namespace Microsoft.AspNetCore.Server.IISIntegration public override unsafe Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + return _httpContext.WriteAsync(new ReadOnlyMemory(buffer, offset, count), cancellationToken); } diff --git a/test/IISIntegration.FunctionalTests/Inprocess/InvalidReadWriteOperationTests.cs b/test/IISIntegration.FunctionalTests/Inprocess/InvalidReadWriteOperationTests.cs index 3d8b995e75..524bc2931f 100644 --- a/test/IISIntegration.FunctionalTests/Inprocess/InvalidReadWriteOperationTests.cs +++ b/test/IISIntegration.FunctionalTests/Inprocess/InvalidReadWriteOperationTests.cs @@ -26,11 +26,13 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests } [ConditionalTheory] + [InlineData("/NullBuffer")] [InlineData("/InvalidOffsetSmall")] [InlineData("/InvalidOffsetLarge")] [InlineData("/InvalidCountSmall")] [InlineData("/InvalidCountLarge")] [InlineData("/InvalidCountWithOffset")] + [InlineData("/InvalidCountZeroRead")] public async Task TestInvalidReadOperations(string operation) { var result = await _fixture.Client.GetStringAsync($"/TestInvalidReadOperations{operation}"); @@ -38,6 +40,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests } [ConditionalTheory] + [InlineData("/NullBuffer")] [InlineData("/InvalidOffsetSmall")] [InlineData("/InvalidOffsetLarge")] [InlineData("/InvalidCountSmall")] diff --git a/test/IISTestSite/Startup.cs b/test/IISTestSite/Startup.cs index 75f94338d6..91f04a7aec 100644 --- a/test/IISTestSite/Startup.cs +++ b/test/IISTestSite/Startup.cs @@ -464,7 +464,22 @@ namespace IISTestSite app.Run(async context => { var success = false; - if (context.Request.Path.StartsWithSegments("/InvalidOffsetSmall")) + if (context.Request.Path.StartsWithSegments("/NullBuffer")) + { + try + { + await context.Request.Body.ReadAsync(null, 0, 0); + } + catch (ArgumentNullException) + { + success = true; + } + catch (Exception) + { + success = true; + } + } + else if (context.Request.Path.StartsWithSegments("/InvalidOffsetSmall")) { try { @@ -519,6 +534,21 @@ namespace IISTestSite success = true; } } + else if (context.Request.Path.StartsWithSegments("/InvalidCountZeroRead")) + { + try + { + await context.Request.Body.ReadAsync(new byte[1], 0, 0); + } + catch (ArgumentOutOfRangeException) + { + success = true; + } + catch (Exception) + { + success = true; + } + } await context.Response.WriteAsync(success ? "Success" : "Failure"); }); @@ -528,7 +558,18 @@ namespace IISTestSite app.Run(async context => { var success = false; - if (context.Request.Path.StartsWithSegments("/InvalidOffsetSmall")) + if (context.Request.Path.StartsWithSegments("/NullBuffer")) + { + try + { + await context.Response.Body.WriteAsync(null, 0, 0); + } + catch (ArgumentNullException) + { + success = true; + } + } + else if (context.Request.Path.StartsWithSegments("/InvalidOffsetSmall")) { try {