diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.ReadWrite.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.ReadWrite.cs index 5329239167..699081e659 100644 --- a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.ReadWrite.cs +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.ReadWrite.cs @@ -17,25 +17,11 @@ namespace Microsoft.AspNetCore.Server.IISIntegration /// /// Reads data from the Input pipe to the user. /// - /// - /// - /// + /// /// /// - public async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + internal async Task ReadAsync(Memory memory, 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) @@ -46,7 +32,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration { if (!readableBuffer.IsEmpty) { - var actual = Math.Min(readableBuffer.Length, count); + var actual = Math.Min(readableBuffer.Length, memory.Length); readableBuffer = readableBuffer.Slice(0, actual); readableBuffer.CopyTo(memory.Span); return (int)actual; @@ -69,7 +55,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration /// /// /// - public Task WriteAsync(ReadOnlyMemory memory, CancellationToken cancellationToken = default(CancellationToken)) + internal Task WriteAsync(ReadOnlyMemory memory, CancellationToken cancellationToken = default(CancellationToken)) { // Want to keep exceptions consistent, @@ -90,7 +76,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration /// /// /// - public Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken)) + internal Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken)) { if (!_hasResponseStarted) { diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.cs index 7690e8450c..32afb07033 100644 --- a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.cs +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.cs @@ -223,7 +223,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration _reasonPhrase = value; } } - + internal IISHttpServer Server { get { return _server; } @@ -333,10 +333,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration { // Verifies we have sent the statuscode before writing a header var reasonPhrase = string.IsNullOrEmpty(ReasonPhrase) ? ReasonPhrases.GetReasonPhrase(StatusCode) : ReasonPhrase; - + // This copies data into the underlying buffer NativeMethods.http_set_response_status_code(_pInProcessHandler, (ushort)StatusCode, reasonPhrase); - + HttpResponseHeaders.IsReadOnly = true; foreach (var headerPair in HttpResponseHeaders) { diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpRequestBody.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpRequestBody.cs index 0a51d9fc7a..be5746c0d3 100644 --- a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpRequestBody.cs +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpRequestBody.cs @@ -39,7 +39,9 @@ namespace Microsoft.AspNetCore.Server.IISIntegration public override unsafe Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { - return _httpContext.ReadAsync(buffer, offset, count, cancellationToken); + var memory = new Memory(buffer, offset, count); + + return _httpContext.ReadAsync(memory, 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 38c8e64c2a..f850994a4a 100644 --- a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpResponseBody.cs +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpResponseBody.cs @@ -54,11 +54,6 @@ 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 524bc2931f..95c05308bd 100644 --- a/test/IISIntegration.FunctionalTests/Inprocess/InvalidReadWriteOperationTests.cs +++ b/test/IISIntegration.FunctionalTests/Inprocess/InvalidReadWriteOperationTests.cs @@ -26,13 +26,11 @@ 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}"); @@ -41,6 +39,23 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests [ConditionalTheory] [InlineData("/NullBuffer")] + [InlineData("/InvalidCountZeroRead")] + public async Task TestValidReadOperations(string operation) + { + var result = await _fixture.Client.GetStringAsync($"/TestValidReadOperations{operation}"); + Assert.Equal("Success", result); + } + + [ConditionalTheory] + [InlineData("/NullBufferPost")] + [InlineData("/InvalidCountZeroReadPost")] + public async Task TestValidReadOperationsPost(string operation) + { + var result = await _fixture.Client.PostAsync($"/TestValidReadOperations{operation}", new StringContent("hello")); + Assert.Equal("Success", await result.Content.ReadAsStringAsync()); + } + + [ConditionalTheory] [InlineData("/InvalidOffsetSmall")] [InlineData("/InvalidOffsetLarge")] [InlineData("/InvalidCountSmall")] @@ -51,5 +66,19 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests var result = await _fixture.Client.GetStringAsync($"/TestInvalidWriteOperations{operation}"); Assert.Equal("Success", result); } + + [ConditionalFact] + public async Task TestValidWriteOperations() + { + var result = await _fixture.Client.GetStringAsync($"/TestValidWriteOperations/NullBuffer"); + Assert.Equal("Success", result); + } + + [ConditionalFact] + public async Task TestValidWriteOperationsPost() + { + var result = await _fixture.Client.PostAsync($"/TestValidWriteOperations/NullBufferPost", new StringContent("hello")); + Assert.Equal("Success", await result.Content.ReadAsStringAsync()); + } } } diff --git a/test/IISIntegration.FunctionalTests/Inprocess/ResponseHeaderTests.cs b/test/IISIntegration.FunctionalTests/Inprocess/ResponseHeaderTests.cs index 6b9e561e40..47ca0dc808 100644 --- a/test/IISIntegration.FunctionalTests/Inprocess/ResponseHeaderTests.cs +++ b/test/IISIntegration.FunctionalTests/Inprocess/ResponseHeaderTests.cs @@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests Assert.Equal("1", headerValues.First()); Assert.Equal("2", headerValues.Last()); } - + [ConditionalFact] public async Task ErrorCodeIsSetForExceptionDuringRequest() { diff --git a/test/IISTestSite/Startup.cs b/test/IISTestSite/Startup.cs index 28460cdaf5..00b83e769d 100644 --- a/test/IISTestSite/Startup.cs +++ b/test/IISTestSite/Startup.cs @@ -470,10 +470,6 @@ namespace IISTestSite { await context.Request.Body.ReadAsync(null, 0, 0); } - catch (ArgumentNullException) - { - success = true; - } catch (Exception) { success = true; @@ -534,42 +530,46 @@ 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"); }); } + + private void TestValidReadOperations(IApplicationBuilder app) + { + app.Run(async context => + { + var count = -1; + + if (context.Request.Path.StartsWithSegments("/NullBuffer")) + { + count = await context.Request.Body.ReadAsync(null, 0, 0); + } + else if (context.Request.Path.StartsWithSegments("/NullBufferPost")) + { + count = await context.Request.Body.ReadAsync(null, 0, 0); + } + else if (context.Request.Path.StartsWithSegments("/InvalidCountZeroRead")) + { + count = await context.Request.Body.ReadAsync(new byte[1], 0, 0); + } + else if (context.Request.Path.StartsWithSegments("/InvalidCountZeroReadPost")) + { + count = await context.Request.Body.ReadAsync(new byte[1], 0, 0); + } + + await context.Response.WriteAsync(count == 0 ? "Success" : "Failure"); + }); + } + private void TestInvalidWriteOperations(IApplicationBuilder app) { app.Run(async context => { var success = false; - 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")) + + if (context.Request.Path.StartsWithSegments("/InvalidOffsetSmall")) { try { @@ -629,6 +629,24 @@ namespace IISTestSite }); } + private void TestValidWriteOperations(IApplicationBuilder app) + { + app.Run(async context => + { + + if (context.Request.Path.StartsWithSegments("/NullBuffer")) + { + await context.Response.Body.WriteAsync(null, 0, 0); + } + else if (context.Request.Path.StartsWithSegments("/NullBufferPost")) + { + await context.Response.Body.WriteAsync(null, 0, 0); + } + + await context.Response.WriteAsync("Success"); + }); + } + private void LargeResponseFile(IApplicationBuilder app) { app.Run(async ctx =>