@@ -146,7 +146,7 @@ public async Task IgnoreNullHeaderValues(string headerName, StringValues headerV
146
146
public async Task OnCompleteCalledEvenWhenOnStartingNotCalled ( )
147
147
{
148
148
var onStartingCalled = false ;
149
- var onCompletedCalled = false ;
149
+ var onCompletedTcs = new TaskCompletionSource < object > ( ) ;
150
150
151
151
var hostBuilder = TransportSelector . GetWebHostBuilder ( )
152
152
. UseKestrel ( )
@@ -156,7 +156,10 @@ public async Task OnCompleteCalledEvenWhenOnStartingNotCalled()
156
156
app . Run ( context =>
157
157
{
158
158
context . Response . OnStarting ( ( ) => Task . Run ( ( ) => onStartingCalled = true ) ) ;
159
- context . Response . OnCompleted ( ( ) => Task . Run ( ( ) => onCompletedCalled = true ) ) ;
159
+ context . Response . OnCompleted ( ( ) => Task . Run ( ( ) =>
160
+ {
161
+ onCompletedTcs . SetResult ( null ) ;
162
+ } ) ) ;
160
163
161
164
// Prevent OnStarting call (see HttpProtocol.ProcessRequestsAsync()).
162
165
throw new Exception ( ) ;
@@ -173,7 +176,7 @@ public async Task OnCompleteCalledEvenWhenOnStartingNotCalled()
173
176
174
177
Assert . Equal ( HttpStatusCode . InternalServerError , response . StatusCode ) ;
175
178
Assert . False ( onStartingCalled ) ;
176
- Assert . True ( onCompletedCalled ) ;
179
+ await onCompletedTcs . Task . TimeoutAfter ( TestConstants . DefaultTimeout ) ;
177
180
}
178
181
}
179
182
}
@@ -294,6 +297,99 @@ public Task ResponseStatusCodeSetBeforeHttpContextDisposedRequestMalformedReadIg
294
297
sendMalformedRequest : true ) ;
295
298
}
296
299
300
+ [ Fact ]
301
+ public async Task OnCompletedExceptionShouldNotPreventAResponse ( )
302
+ {
303
+ var hostBuilder = TransportSelector . GetWebHostBuilder ( )
304
+ . UseKestrel ( )
305
+ . UseUrls ( "http://127.0.0.1:0/" )
306
+ . Configure ( app =>
307
+ {
308
+ app . Run ( async context =>
309
+ {
310
+ context . Response . OnCompleted ( _ => throw new Exception ( ) , null ) ;
311
+ await context . Response . WriteAsync ( "hello, world" ) ;
312
+ } ) ;
313
+ } ) ;
314
+
315
+ using ( var host = hostBuilder . Build ( ) )
316
+ {
317
+ host . Start ( ) ;
318
+
319
+ using ( var client = new HttpClient ( ) )
320
+ {
321
+ var response = await client . GetAsync ( $ "http://127.0.0.1:{ host . GetPort ( ) } /") ;
322
+ Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
323
+ }
324
+ }
325
+ }
326
+
327
+ [ Fact ]
328
+ public async Task OnCompletedShouldNotBlockAResponse ( )
329
+ {
330
+ var delay = Task . Delay ( TestConstants . DefaultTimeout ) ;
331
+ var hostBuilder = TransportSelector . GetWebHostBuilder ( )
332
+ . UseKestrel ( )
333
+ . UseUrls ( "http://127.0.0.1:0/" )
334
+ . Configure ( app =>
335
+ {
336
+ app . Run ( async context =>
337
+ {
338
+ context . Response . OnCompleted ( async ( ) =>
339
+ {
340
+ await delay ;
341
+ } ) ;
342
+ await context . Response . WriteAsync ( "hello, world" ) ;
343
+ } ) ;
344
+ } ) ;
345
+
346
+ using ( var host = hostBuilder . Build ( ) )
347
+ {
348
+ host . Start ( ) ;
349
+
350
+ using ( var client = new HttpClient ( ) )
351
+ {
352
+ var response = await client . GetAsync ( $ "http://127.0.0.1:{ host . GetPort ( ) } /") ;
353
+ Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
354
+ Assert . False ( delay . IsCompleted ) ;
355
+ }
356
+ }
357
+ }
358
+
359
+ [ Fact ]
360
+ public async Task InvalidChunkedEncodingInRequestShouldNotBlockOnCompleted ( )
361
+ {
362
+ var onCompletedTcs = new TaskCompletionSource < object > ( ) ;
363
+
364
+ using ( var server = new TestServer ( httpContext =>
365
+ {
366
+ httpContext . Response . OnCompleted ( ( ) => Task . Run ( ( ) =>
367
+ {
368
+ onCompletedTcs . SetResult ( null ) ;
369
+ } ) ) ;
370
+ return Task . CompletedTask ;
371
+ } ) )
372
+ {
373
+ using ( var connection = server . CreateConnection ( ) )
374
+ {
375
+ await connection . Send (
376
+ "GET / HTTP/1.1" ,
377
+ "Host:" ,
378
+ "Transfer-Encoding: chunked" ,
379
+ "" ,
380
+ "gg" ) ;
381
+ await connection . ReceiveForcedEnd (
382
+ "HTTP/1.1 200 OK" ,
383
+ $ "Date: { server . Context . DateHeaderValue } ",
384
+ "Content-Length: 0" ,
385
+ "" ,
386
+ "" ) ;
387
+ }
388
+ }
389
+
390
+ await onCompletedTcs . Task . TimeoutAfter ( TestConstants . DefaultTimeout ) ;
391
+ }
392
+
297
393
private static async Task ResponseStatusCodeSetBeforeHttpContextDispose (
298
394
RequestDelegate handler ,
299
395
HttpStatusCode ? expectedClientStatusCode ,
@@ -1988,7 +2084,7 @@ await connection.ReceiveEnd(
1988
2084
1989
2085
[ Theory ]
1990
2086
[ MemberData ( nameof ( ConnectionAdapterData ) ) ]
1991
- public async Task ThrowingInOnCompletedIsLoggedAndClosesConnection ( ListenOptions listenOptions )
2087
+ public async Task ThrowingInOnCompletedIsLogged ( ListenOptions listenOptions )
1992
2088
{
1993
2089
var testContext = new TestServiceContext ( ) ;
1994
2090
@@ -2024,7 +2120,7 @@ await connection.Send(
2024
2120
"Host:" ,
2025
2121
"" ,
2026
2122
"" ) ;
2027
- await connection . ReceiveForcedEnd (
2123
+ await connection . ReceiveEnd (
2028
2124
"HTTP/1.1 200 OK" ,
2029
2125
$ "Date: { testContext . DateHeaderValue } ",
2030
2126
"Content-Length: 11" ,
0 commit comments