From b1f0f173aa937bed8910bd42ad92436c0de1b830 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Thu, 6 Sep 2018 10:10:07 -0700 Subject: [PATCH] Use ConnectionResetException with 'The client disconnected' message (#1364) --- build/dependencies.props | 1 + .../Core/IO/AsyncIOOperation.cs | 4 +- .../Microsoft.AspNetCore.Server.IIS.csproj | 3 +- test/IIS.Tests/ClientDisconnectTests.cs | 50 +++++++++++++++++-- test/IIS.Tests/TestServerTest.cs | 22 ++++---- 5 files changed, 62 insertions(+), 18 deletions(-) diff --git a/build/dependencies.props b/build/dependencies.props index 3ecafbac3f..5418510639 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -8,6 +8,7 @@ 2.2.0-preview2-35143 2.2.0-preview2-35143 2.2.0-preview2-35143 + 2.2.0-preview2-35143 2.2.0-preview2-35143 2.2.0-preview2-35143 2.2.0-preview2-35143 diff --git a/src/Microsoft.AspNetCore.Server.IIS/Core/IO/AsyncIOOperation.cs b/src/Microsoft.AspNetCore.Server.IIS/Core/IO/AsyncIOOperation.cs index 9d3752ef08..80e9234ea2 100644 --- a/src/Microsoft.AspNetCore.Server.IIS/Core/IO/AsyncIOOperation.cs +++ b/src/Microsoft.AspNetCore.Server.IIS/Core/IO/AsyncIOOperation.cs @@ -8,6 +8,7 @@ using System.IO; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks.Sources; +using Microsoft.AspNetCore.Connections; namespace Microsoft.AspNetCore.Server.IIS.Core.IO { @@ -103,7 +104,8 @@ namespace Microsoft.AspNetCore.Server.IIS.Core.IO _result = bytes; if (hr != NativeMethods.HR_OK) { - _exception = new IOException("Native IO operation failed", Marshal.GetExceptionForHR(hr)); + // Treat all errors as the client disconnect + _exception = new ConnectionResetException("The client has disconnected", Marshal.GetExceptionForHR(hr)); } } else diff --git a/src/Microsoft.AspNetCore.Server.IIS/Microsoft.AspNetCore.Server.IIS.csproj b/src/Microsoft.AspNetCore.Server.IIS/Microsoft.AspNetCore.Server.IIS.csproj index f9c6fbbd6f..d82d80f9a6 100644 --- a/src/Microsoft.AspNetCore.Server.IIS/Microsoft.AspNetCore.Server.IIS.csproj +++ b/src/Microsoft.AspNetCore.Server.IIS/Microsoft.AspNetCore.Server.IIS.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -24,6 +24,7 @@ + diff --git a/test/IIS.Tests/ClientDisconnectTests.cs b/test/IIS.Tests/ClientDisconnectTests.cs index 72e7adc5bb..c1b2c25062 100644 --- a/test/IIS.Tests/ClientDisconnectTests.cs +++ b/test/IIS.Tests/ClientDisconnectTests.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Extensions.Logging.Testing; @@ -17,7 +18,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests public class ClientDisconnectTests : LoggedTest { - [ConditionalFact(Skip = "See: https://github.com/aspnet/IISIntegration/issues/1075")] + [ConditionalFact] public async Task WritesSucceedAfterClientDisconnect() { var requestStartedCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -49,7 +50,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests } } - [ConditionalFact(Skip = "See: https://github.com/aspnet/IISIntegration/issues/1075")] + [ConditionalFact] public async Task ReadThrowsAfterClientDisconnect() { var requestStartedCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -82,8 +83,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests await requestCompletedCompletionSource.Task.TimeoutAfterDefault(); } - Assert.IsType(exception); - Assert.Equal("Native IO operation failed", exception.Message); + Assert.IsType(exception); + Assert.Equal("The client has disconnected", exception.Message); } [ConditionalFact(Skip = "See: https://github.com/aspnet/IISIntegration/issues/1075")] @@ -126,7 +127,6 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests Assert.IsType(exception); } } - [ConditionalFact(Skip = "See: https://github.com/aspnet/IISIntegration/issues/1075")] public async Task ReaderThrowsCancelledException() { @@ -163,6 +163,46 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests } } + [ConditionalFact] + public async Task ReaderThrowsResetExceptionOnInvalidBody() + { + var requestCompletedCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + Exception exception = null; + + var data = new byte[1024]; + using (var testServer = await TestServer.Create(async ctx => + { + try + { + await ctx.Request.Body.ReadAsync(data); + } + catch (Exception e) + { + exception = e; + } + + requestCompletedCompletionSource.SetResult(true); + }, LoggerFactory)) + { + using (var connection = testServer.CreateConnection()) + { + await connection.Send( + "POST / HTTP/1.1", + "Transfer-Encoding: chunked", + "Host: localhost", + "Connection: close", + "", + "ZZZ", + ""); + await requestCompletedCompletionSource.Task.TimeoutAfterDefault(); + } + } + + Assert.IsType(exception); + Assert.Equal("The client has disconnected", exception.Message); + } + private static async Task SendContentLength1Post(TestConnection connection) { await connection.Send( diff --git a/test/IIS.Tests/TestServerTest.cs b/test/IIS.Tests/TestServerTest.cs index 79b1c42113..c168af99d8 100644 --- a/test/IIS.Tests/TestServerTest.cs +++ b/test/IIS.Tests/TestServerTest.cs @@ -19,17 +19,17 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests var helloWorld = "Hello World"; var expectedPath = "/Path"; - string path = null; - using (var testServer = await TestServer.Create(ctx => - { - path = ctx.Request.Path.ToString(); - return ctx.Response.WriteAsync(helloWorld); - }, LoggerFactory)) - { - var result = await testServer.HttpClient.GetAsync(expectedPath); - Assert.Equal(helloWorld, await result.Content.ReadAsStringAsync()); - Assert.Equal(expectedPath, path); - } + string path = null; + using (var testServer = await TestServer.Create(ctx => + { + path = ctx.Request.Path.ToString(); + return ctx.Response.WriteAsync(helloWorld); + }, LoggerFactory)) + { + var result = await testServer.HttpClient.GetAsync(expectedPath); + Assert.Equal(helloWorld, await result.Content.ReadAsStringAsync()); + Assert.Equal(expectedPath, path); } } + } }