Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Commit 9c476b1

Browse files
mikeharderCesar Blum Silveira
authored andcommitted
Ensure status code is 500 if app calls HttpContext.Abort() and then throws an exception
1 parent 89e0925 commit 9c476b1

File tree

2 files changed

+70
-18
lines changed
  • src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http
  • test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests

2 files changed

+70
-18
lines changed

src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/FrameOfT.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,13 @@ public override async Task RequestProcessingAsync()
126126
await messageBody.Consume();
127127
}
128128

129-
// ProduceEnd() must be called before _application.DisposeContext(), to ensure
130-
// HttpContext.Response.StatusCode is correctly set when
131-
// IHttpContextFactory.Dispose(HttpContext) is called.
132-
await ProduceEnd();
133129
}
134130

131+
// ProduceEnd() must be called before _application.DisposeContext(), to ensure
132+
// HttpContext.Response.StatusCode is correctly set when
133+
// IHttpContextFactory.Dispose(HttpContext) is called.
134+
await ProduceEnd();
135+
135136
_application.DisposeContext(context, _applicationException);
136137
}
137138

test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ResponseTests.cs

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
1616
using Microsoft.AspNetCore.Testing;
1717
using Microsoft.Extensions.DependencyInjection;
18+
using Microsoft.Extensions.Internal;
1819
using Microsoft.Extensions.Primitives;
1920
using Moq;
2021
using Xunit;
@@ -86,7 +87,7 @@ public async Task IgnoreNullHeaderValues(string headerName, StringValues headerV
8687
app.Run(async context =>
8788
{
8889
context.Response.Headers.Add(headerName, headerValue);
89-
90+
9091
await context.Response.WriteAsync("");
9192
});
9293
});
@@ -176,35 +177,73 @@ public async Task OnStartingThrowsWhenSetAfterResponseHasAlreadyStarted()
176177
using (var client = new HttpClient())
177178
{
178179
var response = await client.GetAsync($"http://localhost:{host.GetPort()}/");
179-
180+
180181
// Despite the error, the response had already started
181182
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
182183
Assert.NotNull(ex);
183184
}
184185
}
185186
}
186-
187+
187188
[Fact]
188-
public async Task ResponseStatusCodeSetBeforeHttpContextDispose()
189+
public Task ResponseStatusCodeSetBeforeHttpContextDisposeAppException()
190+
{
191+
return ResponseStatusCodeSetBeforeHttpContextDispose(
192+
context =>
193+
{
194+
throw new Exception();
195+
},
196+
expectedClientStatusCode: HttpStatusCode.InternalServerError,
197+
expectedServerStatusCode: HttpStatusCode.InternalServerError);
198+
}
199+
200+
[Fact]
201+
public Task ResponseStatusCodeSetBeforeHttpContextDisposeRequestAborted()
202+
{
203+
return ResponseStatusCodeSetBeforeHttpContextDispose(
204+
context =>
205+
{
206+
context.Abort();
207+
return TaskCache.CompletedTask;
208+
},
209+
expectedClientStatusCode: null,
210+
expectedServerStatusCode: HttpStatusCode.OK);
211+
}
212+
213+
[Fact]
214+
public Task ResponseStatusCodeSetBeforeHttpContextDisposeRequestAbortedAppException()
215+
{
216+
return ResponseStatusCodeSetBeforeHttpContextDispose(
217+
context =>
218+
{
219+
context.Abort();
220+
throw new Exception();
221+
},
222+
expectedClientStatusCode: null,
223+
expectedServerStatusCode: HttpStatusCode.InternalServerError);
224+
}
225+
226+
private static async Task ResponseStatusCodeSetBeforeHttpContextDispose(RequestDelegate handler,
227+
HttpStatusCode? expectedClientStatusCode, HttpStatusCode expectedServerStatusCode)
189228
{
190229
var mockHttpContextFactory = new Mock<IHttpContextFactory>();
191230
mockHttpContextFactory.Setup(f => f.Create(It.IsAny<IFeatureCollection>()))
192231
.Returns<IFeatureCollection>(fc => new DefaultHttpContext(fc));
193232

194-
int disposedStatusCode = -1;
233+
var disposedTcs = new TaskCompletionSource<int>();
195234
mockHttpContextFactory.Setup(f => f.Dispose(It.IsAny<HttpContext>()))
196-
.Callback<HttpContext>(c => disposedStatusCode = c.Response.StatusCode);
235+
.Callback<HttpContext>(c =>
236+
{
237+
disposedTcs.TrySetResult(c.Response.StatusCode);
238+
});
197239

198240
var hostBuilder = new WebHostBuilder()
199241
.UseKestrel()
200242
.UseUrls("http://127.0.0.1:0")
201243
.ConfigureServices(services => services.AddSingleton<IHttpContextFactory>(mockHttpContextFactory.Object))
202244
.Configure(app =>
203245
{
204-
app.Run(context =>
205-
{
206-
throw new Exception();
207-
});
246+
app.Run(handler);
208247
});
209248

210249
using (var host = hostBuilder.Build())
@@ -213,12 +252,24 @@ public async Task ResponseStatusCodeSetBeforeHttpContextDispose()
213252

214253
using (var client = new HttpClient())
215254
{
216-
var response = await client.GetAsync($"http://localhost:{host.GetPort()}/");
217-
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
255+
try
256+
{
257+
var response = await client.GetAsync($"http://localhost:{host.GetPort()}/");
258+
Assert.Equal(expectedClientStatusCode, response.StatusCode);
259+
}
260+
catch
261+
{
262+
if (expectedClientStatusCode != null)
263+
{
264+
throw;
265+
}
266+
}
218267
}
219-
}
220268

221-
Assert.Equal(HttpStatusCode.InternalServerError, (HttpStatusCode)disposedStatusCode);
269+
var disposedStatusCode = await disposedTcs.Task.TimeoutAfter(TimeSpan.FromSeconds(10));
270+
271+
Assert.Equal(expectedServerStatusCode, (HttpStatusCode)disposedStatusCode);
272+
}
222273
}
223274

224275
// https://github.com/aspnet/KestrelHttpServer/pull/1111/files#r80584475 explains the reason for this test.

0 commit comments

Comments
 (0)