File tree Expand file tree Collapse file tree 2 files changed +52
-0
lines changed
src/Microsoft.Owin.Host.HttpListener
tests/Microsoft.Owin.Host.HttpListener.Tests Expand file tree Collapse file tree 2 files changed +52
-0
lines changed Original file line number Diff line number Diff line change @@ -16,6 +16,9 @@ namespace Microsoft.Owin.Host.HttpListener
16
16
17
17
internal class DisconnectHandler
18
18
{
19
+ // Win8 minimum
20
+ private static bool SkipIOCPCallbackOnSuccess = Environment . OSVersion . Version >= new Version ( 6 , 2 ) ;
21
+
19
22
private readonly ConcurrentDictionary < ulong , ConnectionCancellation > _connectionCancellationTokens ;
20
23
private readonly System . Net . HttpListener _listener ;
21
24
private readonly CriticalHandle _requestQueueHandle ;
@@ -114,6 +117,15 @@ private unsafe CancellationToken CreateToken(ulong connectionId)
114
117
cts . Cancel ( ) ;
115
118
}
116
119
120
+ if ( hr == NativeMethods . HttpErrors . NO_ERROR && SkipIOCPCallbackOnSuccess )
121
+ {
122
+ // IO operation completed synchronously - callback won't be called to signal completion
123
+ Overlapped . Free ( nativeOverlapped ) ;
124
+ ConnectionCancellation cancellation ;
125
+ _connectionCancellationTokens . TryRemove ( connectionId , out cancellation ) ;
126
+ cts . Cancel ( ) ;
127
+ }
128
+
117
129
return returnToken ;
118
130
}
119
131
Original file line number Diff line number Diff line change @@ -342,6 +342,46 @@ public void Disconnect_ClientDisconnects_EventFires()
342
342
}
343
343
}
344
344
345
+ [ Fact ]
346
+ public void Disconnect_ClientDisconnects_Before_CancellationToken_Created ( )
347
+ {
348
+ var requestReceived = new ManualResetEvent ( false ) ;
349
+ var requestCanceled = new ManualResetEvent ( false ) ;
350
+
351
+ var clientDisposed = new ManualResetEvent ( false ) ;
352
+
353
+ OwinHttpListener listener = CreateServer (
354
+ env =>
355
+ {
356
+ requestReceived . Set ( ) ;
357
+
358
+ // lets wait for client to be gone
359
+ Assert . True ( clientDisposed . WaitOne ( 1000 ) ) ;
360
+
361
+ // the most important part is not to observe CancellationToken before client disconnects
362
+
363
+ GetCallCancelled ( env ) . Register ( ( ) => requestCanceled . Set ( ) ) ;
364
+ return Task . FromResult ( 0 ) ;
365
+ } ,
366
+ HttpServerAddress ) ;
367
+
368
+ using ( listener )
369
+ {
370
+ using ( var client = new HttpClient ( ) )
371
+ {
372
+ var requestTask = client . GetAsync ( HttpClientAddress ) ;
373
+ Assert . True ( requestReceived . WaitOne ( 1000 ) ) ;
374
+ client . CancelPendingRequests ( ) ;
375
+
376
+ Assert . Throws < AggregateException > ( ( ) => requestTask . Result ) ;
377
+ }
378
+
379
+ clientDisposed . Set ( ) ;
380
+
381
+ Assert . True ( requestCanceled . WaitOne ( 1000 ) ) ;
382
+ }
383
+ }
384
+
345
385
private static CancellationToken GetCallCancelled ( IDictionary < string , object > env )
346
386
{
347
387
return env . Get < CancellationToken > ( "owin.CallCancelled" ) ;
You can’t perform that action at this time.
0 commit comments