This commit is contained in:
Chris Ross 2018-01-29 16:04:24 -08:00
Родитель 0156279603
Коммит 5d6e25b303
2 изменённых файлов: 52 добавлений и 0 удалений

Просмотреть файл

@ -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");