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:
parent
b21f84543a
commit
9072e0ba26
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue