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
1616
1717 internal class DisconnectHandler
1818 {
19+ // Win8 minimum
20+ private static bool SkipIOCPCallbackOnSuccess = Environment . OSVersion . Version >= new Version ( 6 , 2 ) ;
21+
1922 private readonly ConcurrentDictionary < ulong , ConnectionCancellation > _connectionCancellationTokens ;
2023 private readonly System . Net . HttpListener _listener ;
2124 private readonly CriticalHandle _requestQueueHandle ;
@@ -114,6 +117,15 @@ private unsafe CancellationToken CreateToken(ulong connectionId)
114117 cts . Cancel ( ) ;
115118 }
116119
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+
117129 return returnToken ;
118130 }
119131
Original file line number Diff line number Diff line change @@ -342,6 +342,46 @@ public void Disconnect_ClientDisconnects_EventFires()
342342 }
343343 }
344344
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+
345385 private static CancellationToken GetCallCancelled ( IDictionary < string , object > env )
346386 {
347387 return env . Get < CancellationToken > ( "owin.CallCancelled" ) ;
You can’t perform that action at this time.
0 commit comments