Handle early disconnects #141
This commit is contained in:
Родитель
0156279603
Коммит
5d6e25b303
|
@ -16,6 +16,9 @@ namespace Microsoft.Owin.Host.HttpListener
|
|||
|
||||
internal class DisconnectHandler
|
||||
{
|
||||
// Win8 minimum
|
||||
private static bool SkipIOCPCallbackOnSuccess = Environment.OSVersion.Version >= new Version(6, 2);
|
||||
|
||||
private readonly ConcurrentDictionary<ulong, ConnectionCancellation> _connectionCancellationTokens;
|
||||
private readonly System.Net.HttpListener _listener;
|
||||
private readonly CriticalHandle _requestQueueHandle;
|
||||
|
@ -114,6 +117,15 @@ namespace Microsoft.Owin.Host.HttpListener
|
|||
cts.Cancel();
|
||||
}
|
||||
|
||||
if (hr == NativeMethods.HttpErrors.NO_ERROR && SkipIOCPCallbackOnSuccess)
|
||||
{
|
||||
// IO operation completed synchronously - callback won't be called to signal completion
|
||||
Overlapped.Free(nativeOverlapped);
|
||||
ConnectionCancellation cancellation;
|
||||
_connectionCancellationTokens.TryRemove(connectionId, out cancellation);
|
||||
cts.Cancel();
|
||||
}
|
||||
|
||||
return returnToken;
|
||||
}
|
||||
|
||||
|
|
|
@ -342,6 +342,46 @@ namespace Microsoft.Owin.Host.HttpListener.Tests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Disconnect_ClientDisconnects_Before_CancellationToken_Created()
|
||||
{
|
||||
var requestReceived = new ManualResetEvent(false);
|
||||
var requestCanceled = new ManualResetEvent(false);
|
||||
|
||||
var clientDisposed = new ManualResetEvent(false);
|
||||
|
||||
OwinHttpListener listener = CreateServer(
|
||||
env =>
|
||||
{
|
||||
requestReceived.Set();
|
||||
|
||||
// lets wait for client to be gone
|
||||
Assert.True(clientDisposed.WaitOne(1000));
|
||||
|
||||
// the most important part is not to observe CancellationToken before client disconnects
|
||||
|
||||
GetCallCancelled(env).Register(() => requestCanceled.Set());
|
||||
return Task.FromResult(0);
|
||||
},
|
||||
HttpServerAddress);
|
||||
|
||||
using (listener)
|
||||
{
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
var requestTask = client.GetAsync(HttpClientAddress);
|
||||
Assert.True(requestReceived.WaitOne(1000));
|
||||
client.CancelPendingRequests();
|
||||
|
||||
Assert.Throws<AggregateException>(() => requestTask.Result);
|
||||
}
|
||||
|
||||
clientDisposed.Set();
|
||||
|
||||
Assert.True(requestCanceled.WaitOne(1000));
|
||||
}
|
||||
}
|
||||
|
||||
private static CancellationToken GetCallCancelled(IDictionary<string, object> env)
|
||||
{
|
||||
return env.Get<CancellationToken>("owin.CallCancelled");
|
||||
|
|
Загрузка…
Ссылка в новой задаче