@@ -146,7 +146,7 @@ public async Task IgnoreNullHeaderValues(string headerName, StringValues headerV
146146 public async Task OnCompleteCalledEvenWhenOnStartingNotCalled ( )
147147 {
148148 var onStartingCalled = false ;
149- var onCompletedCalled = false ;
149+ var onCompletedTcs = new TaskCompletionSource < object > ( ) ;
150150
151151 var hostBuilder = TransportSelector . GetWebHostBuilder ( )
152152 . UseKestrel ( )
@@ -156,7 +156,10 @@ public async Task OnCompleteCalledEvenWhenOnStartingNotCalled()
156156 app . Run ( context =>
157157 {
158158 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+ } ) ) ;
160163
161164 // Prevent OnStarting call (see HttpProtocol.ProcessRequestsAsync()).
162165 throw new Exception ( ) ;
@@ -173,7 +176,7 @@ public async Task OnCompleteCalledEvenWhenOnStartingNotCalled()
173176
174177 Assert . Equal ( HttpStatusCode . InternalServerError , response . StatusCode ) ;
175178 Assert . False ( onStartingCalled ) ;
176- Assert . True ( onCompletedCalled ) ;
179+ await onCompletedTcs . Task . TimeoutAfter ( TestConstants . DefaultTimeout ) ;
177180 }
178181 }
179182 }
@@ -294,6 +297,99 @@ public Task ResponseStatusCodeSetBeforeHttpContextDisposedRequestMalformedReadIg
294297 sendMalformedRequest : true ) ;
295298 }
296299
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+
297393 private static async Task ResponseStatusCodeSetBeforeHttpContextDispose (
298394 RequestDelegate handler ,
299395 HttpStatusCode ? expectedClientStatusCode ,
@@ -1988,7 +2084,7 @@ await connection.ReceiveEnd(
19882084
19892085 [ Theory ]
19902086 [ MemberData ( nameof ( ConnectionAdapterData ) ) ]
1991- public async Task ThrowingInOnCompletedIsLoggedAndClosesConnection ( ListenOptions listenOptions )
2087+ public async Task ThrowingInOnCompletedIsLogged ( ListenOptions listenOptions )
19922088 {
19932089 var testContext = new TestServiceContext ( ) ;
19942090
@@ -2024,7 +2120,7 @@ await connection.Send(
20242120 "Host:" ,
20252121 "" ,
20262122 "" ) ;
2027- await connection . ReceiveForcedEnd (
2123+ await connection . ReceiveEnd (
20282124 "HTTP/1.1 200 OK" ,
20292125 $ "Date: { testContext . DateHeaderValue } ",
20302126 "Content-Length: 11" ,
0 commit comments