From a97cb81f9282edfccd3b6768d9c0242e85e97e6a Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 22 Jan 2016 13:59:46 +0000 Subject: [PATCH] MockConnection Abort --- .../Http/Connection.cs | 2 +- .../Http/SocketOutput.cs | 23 ++++++++++++++----- .../TestHelpers/MockConnection.cs | 18 +++++++++++++++ .../SocketOutputTests.cs | 4 ++-- 4 files changed, 38 insertions(+), 9 deletions(-) create mode 100644 test/Microsoft.AspNet.Server.KestrelTests/TestHelpers/MockConnection.cs diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs index 52790ec65a..0708796ecb 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs @@ -109,7 +109,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http } } - public void Abort() + public virtual void Abort() { if (_frame != null) { diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/SocketOutput.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/SocketOutput.cs index 38533f447b..b724da9e5a 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/SocketOutput.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/SocketOutput.cs @@ -45,6 +45,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http // The number of write operations that have been scheduled so far // but have not completed. private bool _writePending = false; + private bool _cancelled = false; private int _numBytesPreCompleted = 0; private Exception _lastWriteError; private WriteContext _nextWriteContext; @@ -90,7 +91,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http lock (_contextLock) { - if (_lastWriteError != null || _socket.IsClosed) + if (_socket.IsClosed) { _log.ConnectionDisconnectedWrite(_connectionId, buffer.Count, _lastWriteError); @@ -164,7 +165,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http if (cancellationToken.IsCancellationRequested) { _connection.Abort(); - + _cancelled = true; return TaskUtilities.GetCancelledTask(cancellationToken); } else @@ -295,7 +296,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http { // Abort the connection for any failed write // Queued on threadpool so get it in as first op. - _connection?.Abort(); + _connection.Abort(); + _cancelled = true; CompleteAllWrites(); } @@ -346,9 +348,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http } // This may called on the libuv event loop - // This is always called with the _contextLock already acquired private void OnWriteCompleted(WriteContext writeContext) { + // Called inside _contextLock var bytesWritten = writeContext.ByteCount; var status = writeContext.WriteStatus; var error = writeContext.WriteError; @@ -358,6 +360,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http // Abort the connection for any failed write // Queued on threadpool so get it in as first op. _connection.Abort(); + _cancelled = true; _lastWriteError = error; } @@ -381,6 +384,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http private void CompleteNextWrite(ref int bytesLeftToBuffer) { + // Called inside _contextLock var waitingTask = _tasksPending.Dequeue(); var bytesToWrite = waitingTask.BytesToWrite; @@ -416,6 +420,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http private void CompleteFinishedWrites(int status) { + // Called inside _contextLock // bytesLeftToBuffer can be greater than _maxBytesPreCompleted // This allows large writes to complete once they've actually finished. var bytesLeftToBuffer = _maxBytesPreCompleted - _numBytesPreCompleted; @@ -428,6 +433,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http private void CompleteAllWrites() { + // Called inside _contextLock var writesToComplete = _tasksPending.Count > 0; var bytesLeftToBuffer = _maxBytesPreCompleted - _numBytesPreCompleted; while (_tasksPending.Count > 0) @@ -468,7 +474,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http private void PoolWriteContext(WriteContext writeContext) { - // called inside _contextLock + // Called inside _contextLock if (_writeContextPool.Count < _maxPooledWriteContexts) { writeContext.Reset(); @@ -485,9 +491,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http { if (cancellationToken.IsCancellationRequested) { - _connection?.Abort(); + _connection.Abort(); + _cancelled = true; return TaskUtilities.GetCancelledTask(cancellationToken); } + else if (_cancelled) + { + return TaskUtilities.CompletedTask; + } return WriteAsync(buffer, cancellationToken, immediate, chunk); } diff --git a/test/Microsoft.AspNet.Server.KestrelTests/TestHelpers/MockConnection.cs b/test/Microsoft.AspNet.Server.KestrelTests/TestHelpers/MockConnection.cs new file mode 100644 index 0000000000..e11e354f60 --- /dev/null +++ b/test/Microsoft.AspNet.Server.KestrelTests/TestHelpers/MockConnection.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNet.Server.Kestrel.Http; +using Microsoft.AspNet.Server.Kestrel.Networking; + +namespace Microsoft.AspNet.Server.KestrelTests.TestHelpers +{ + public class MockConnection : Connection + { + public MockConnection(UvStreamHandle socket) + : base (new ListenerContext(), socket) + { + + } + + public override void Abort() + { + } + } +} diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/SocketOutputTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/SocketOutputTests.cs index ec0fe0c9a0..4d24150567 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/SocketOutputTests.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/SocketOutputTests.cs @@ -224,7 +224,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests var socket = new MockSocket(kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); var ltp = new LoggingThreadPool(trace); - ISocketOutput socketOutput = new SocketOutput(kestrelThread, socket, memory, null, 0, trace, ltp, new Queue()); + ISocketOutput socketOutput = new SocketOutput(kestrelThread, socket, memory, new MockConnection(socket), 0, trace, ltp, new Queue()); var bufferSize = maxBytesPreCompleted; @@ -302,7 +302,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests Assert.False(task6Throw.IsCanceled); Assert.False(task6Throw.IsFaulted); - Assert.Throws(() => task6Throw.GetAwaiter().GetResult()); + task6Throw.GetAwaiter().GetResult(); Assert.True(true); }