From f6cda4fab73ff5d4594741cc5b34b58fa3d0e579 Mon Sep 17 00:00:00 2001 From: "Chris Ross (ASP.NET)" Date: Fri, 16 Nov 2018 16:39:50 -0800 Subject: [PATCH] Make TestServer handle exceptions from OnStarting #1594 --- .../HttpContextBuilder.cs | 11 +++- .../ClientHandlerTests.cs | 57 +++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.AspNetCore.TestHost/HttpContextBuilder.cs b/src/Microsoft.AspNetCore.TestHost/HttpContextBuilder.cs index 6886b1aac4..c576628b65 100644 --- a/src/Microsoft.AspNetCore.TestHost/HttpContextBuilder.cs +++ b/src/Microsoft.AspNetCore.TestHost/HttpContextBuilder.cs @@ -109,7 +109,16 @@ namespace Microsoft.AspNetCore.TestHost if (!_responseFeature.HasStarted) { // Sets HasStarted - await _responseFeature.FireOnSendingHeadersAsync(); + try + { + await _responseFeature.FireOnSendingHeadersAsync(); + } + catch (Exception ex) + { + Abort(ex); + return; + } + // Copy the feature collection so we're not multi-threading on the same collection. var newFeatures = new FeatureCollection(); foreach (var pair in _httpContext.Features) diff --git a/test/Microsoft.AspNetCore.TestHost.Tests/ClientHandlerTests.cs b/test/Microsoft.AspNetCore.TestHost.Tests/ClientHandlerTests.cs index e90a79e97b..7187f493dc 100644 --- a/test/Microsoft.AspNetCore.TestHost.Tests/ClientHandlerTests.cs +++ b/test/Microsoft.AspNetCore.TestHost.Tests/ClientHandlerTests.cs @@ -272,6 +272,63 @@ namespace Microsoft.AspNetCore.TestHost Assert.IsType(ex.GetBaseException()); } + [Fact] + public Task ExceptionFromOnStartingFirstWriteIsReported() + { + var handler = new ClientHandler(PathString.Empty, new DummyApplication(context => + { + context.Response.OnStarting(() => + { + throw new InvalidOperationException(new string('a', 1024 * 32)); + }); + return context.Response.WriteAsync("Hello World"); + })); + var httpClient = new HttpClient(handler); + return Assert.ThrowsAsync(() => httpClient.GetAsync("https://example.com/", + HttpCompletionOption.ResponseHeadersRead)); + } + + [Fact] + public Task ExceptionFromOnStartingWithNoWriteIsReported() + { + var handler = new ClientHandler(PathString.Empty, new DummyApplication(context => + { + context.Response.OnStarting(() => + { + throw new InvalidOperationException(new string('a', 1024 * 32)); + }); + return Task.CompletedTask; + })); + var httpClient = new HttpClient(handler); + return Assert.ThrowsAsync(() => httpClient.GetAsync("https://example.com/", + HttpCompletionOption.ResponseHeadersRead)); + } + + [Fact] + public Task ExceptionFromOnStartingWithErrorHandlerIsReported() + { + var handler = new ClientHandler(PathString.Empty, new DummyApplication(async context => + { + context.Response.OnStarting(() => + { + throw new InvalidOperationException(new string('a', 1024 * 32)); + }); + try + { + await context.Response.WriteAsync("Hello World"); + } + catch (Exception ex) + { + // This is no longer the first write, so it doesn't trigger OnStarting again. + // The exception is large enough that it fills the pipe and stalls. + await context.Response.WriteAsync(ex.ToString()); + } + })); + var httpClient = new HttpClient(handler); + return Assert.ThrowsAsync(() => httpClient.GetAsync("https://example.com/", + HttpCompletionOption.ResponseHeadersRead)); + } + private class DummyApplication : IHttpApplication { RequestDelegate _application;