Added a ConnectionAbortedException to Transport.Abstractions (#1806)

* Added a ConnectionAbortedException to Transport.Abstractions
- To avoid hard coding TaskCanceledException in each transport
- This PR tries to keep compatibility by converting the ConnectionAbortedException
to a TaskCanceledException on exceptions in FrameRequestStream. The downside is that
this conversion causes an async state machine to be created per call to ReadAsync.
CopyToAsync isn't that bad because it's a single long running task.
This commit is contained in:
David Fowler 2017-05-03 19:38:34 -07:00 committed by GitHub
parent b21f84543a
commit 9072e0ba26
5 changed files with 61 additions and 11 deletions

View File

@ -3,9 +3,11 @@
using System;
using System.IO;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
@ -105,11 +107,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
var task = ValidateState(cancellationToken);
if (task == null)
if (task != null)
{
return _body.ReadAsync(new ArraySegment<byte>(buffer, offset, count), cancellationToken);
return task;
}
return ReadAsyncInternal(buffer, offset, count, cancellationToken);
}
private async Task<int> ReadAsyncInternal(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
try
{
return await _body.ReadAsync(new ArraySegment<byte>(buffer, offset, count), cancellationToken);
}
catch (ConnectionAbortedException ex)
{
throw new TaskCanceledException("The request was aborted", ex);
}
return task;
}
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
@ -124,11 +139,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
}
var task = ValidateState(cancellationToken);
if (task == null)
if (task != null)
{
return _body.CopyToAsync(destination, cancellationToken);
return task;
}
return CopyToAsyncInternal(destination, cancellationToken);
}
private async Task CopyToAsyncInternal(Stream destination, CancellationToken cancellationToken)
{
try
{
await _body.CopyToAsync(destination, cancellationToken);
}
catch (ConnectionAbortedException ex)
{
throw new TaskCanceledException("The request was aborted", ex);
}
return task;
}
public void StartAcceptingReads(MessageBody body)
@ -165,7 +193,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
public void Abort(Exception error = null)
{
// We don't want to throw an ODE until the app func actually completes.
// If the request is aborted, we throw an TaskCanceledException instead,
// If the request is aborted, we throw a TaskCanceledException instead,
// unless error is not null, in which case we throw it.
if (_state != FrameStreamState.Closed)
{

View File

@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
{
@ -25,7 +26,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
public static async Task<bool> AbortAllConnectionsAsync(this FrameConnectionManager connectionManager)
{
var abortTasks = new List<Task>();
var canceledException = new TaskCanceledException(CoreStrings.RequestProcessingAborted);
var canceledException = new ConnectionAbortedException();
connectionManager.Walk(connection =>
{

View File

@ -0,0 +1,21 @@
using System;
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions
{
public class ConnectionAbortedException : OperationCanceledException
{
public ConnectionAbortedException() :
this("The connection was aborted")
{
}
public ConnectionAbortedException(string message) : base(message)
{
}
public ConnectionAbortedException(string message, Exception inner) : base(message, inner)
{
}
}
}

View File

@ -86,7 +86,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
Input.CancelPendingFlush();
// Now, complete the input so that no more reads can happen
Input.Complete(new TaskCanceledException("The request was aborted"));
Input.Complete(new ConnectionAbortedException());
// We're done with the socket now
_socket.Dispose();

View File

@ -115,11 +115,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
}
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted)
{
error = new TaskCanceledException("The request was aborted");
error = new ConnectionAbortedException();
}
catch (ObjectDisposedException)
{
error = new TaskCanceledException("The request was aborted");
error = new ConnectionAbortedException();
}
catch (IOException ex)
{