From 874bd29ce192a28ddae7d5f0bee66a5c595fa829 Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Tue, 3 Nov 2015 14:02:28 -0800 Subject: [PATCH] Throw IOExceptions instead of ODEs after a request is aborted --- .../Http/Frame.cs | 4 +-- .../Http/FrameRequestStream.cs | 29 +++++++++++++++++++ .../Http/FrameResponseStream.cs | 24 +++++++++++++++ .../TestInput.cs | 4 +++ 4 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index 39f4e428ec..521140bfdf 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -183,8 +183,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http _requestProcessingStopping = true; _requestAborted = true; - _requestBody?.StopAcceptingReads(); - _responseBody?.StopAcceptingWrites(); + _requestBody?.Abort(); + _responseBody?.Abort(); try { diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameRequestStream.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameRequestStream.cs index 8483020ddb..652f2bbd4b 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameRequestStream.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameRequestStream.cs @@ -12,6 +12,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { private readonly MessageBody _body; private bool _stopped; + private bool _aborted; public FrameRequestStream(MessageBody body) { @@ -55,6 +56,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { throw new ObjectDisposedException(nameof(FrameRequestStream)); } + if (_aborted) + { + throw new IOException("The request has been aborted."); + } return ReadAsync(buffer, offset, count).GetAwaiter().GetResult(); } @@ -66,6 +71,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { throw new ObjectDisposedException(nameof(FrameRequestStream)); } + if (_aborted) + { + throw new IOException("The request has been aborted."); + } var task = ReadAsync(buffer, offset, count, CancellationToken.None, state); if (callback != null) @@ -86,6 +95,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { throw new ObjectDisposedException(nameof(FrameRequestStream)); } + if (_aborted) + { + throw new IOException("The request has been aborted."); + } var tcs = new TaskCompletionSource(state); var task = _body.ReadAsync(new ArraySegment(buffer, offset, count), cancellationToken); @@ -111,6 +124,15 @@ namespace Microsoft.AspNet.Server.Kestrel.Http public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { + if (_stopped) + { + throw new ObjectDisposedException(nameof(FrameRequestStream)); + } + if (_aborted) + { + throw new IOException("The request has been aborted."); + } + return _body.ReadAsync(new ArraySegment(buffer, offset, count), cancellationToken); } @@ -125,5 +147,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http // As exampled in EngineTests.ZeroContentLengthNotSetAutomaticallyForCertainStatusCodes _stopped = true; } + + public void Abort() + { + // We don't want to throw an ODE until the app func actually completes. + // If the request is aborted, we throw an IOException instead. + _aborted = true; + } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseStream.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseStream.cs index bef3453ad3..25b8a72cfe 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseStream.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseStream.cs @@ -12,6 +12,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { private readonly FrameContext _context; private bool _stopped; + private bool _aborted; public FrameResponseStream(FrameContext context) { @@ -40,6 +41,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { throw new ObjectDisposedException(nameof(FrameResponseStream)); } + if (_aborted) + { + throw new IOException("The request has been aborted."); + } _context.FrameControl.Flush(); } @@ -50,6 +55,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { throw new ObjectDisposedException(nameof(FrameResponseStream)); } + if (_aborted) + { + throw new IOException("The request has been aborted."); + } return _context.FrameControl.FlushAsync(cancellationToken); } @@ -75,6 +84,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { throw new ObjectDisposedException(nameof(FrameResponseStream)); } + if (_aborted) + { + throw new IOException("The request has been aborted."); + } _context.FrameControl.Write(new ArraySegment(buffer, offset, count)); } @@ -85,6 +98,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { throw new ObjectDisposedException(nameof(FrameResponseStream)); } + if (_aborted) + { + throw new IOException("The request has been aborted."); + } return _context.FrameControl.WriteAsync(new ArraySegment(buffer, offset, count), cancellationToken); } @@ -95,5 +112,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http // As exampled in EngineTests.ZeroContentLengthNotSetAutomaticallyForCertainStatusCodes _stopped = true; } + + public void Abort() + { + // We don't want to throw an ODE until the app func actually completes. + // If the request is aborted, we throw an IOException instead. + _aborted = true; + } } } diff --git a/test/Microsoft.AspNet.Server.KestrelTests/TestInput.cs b/test/Microsoft.AspNet.Server.KestrelTests/TestInput.cs index 3db112560c..24cf1cdb77 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/TestInput.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/TestInput.cs @@ -51,6 +51,10 @@ namespace Microsoft.AspNet.Server.KestrelTests { } + public void Abort() + { + } + public void Write(ArraySegment data, Action callback, object state) { }