Handle incoming HTTP requests being canceled gracefully (#2314)

This commit is contained in:
James Newton-King 2018-05-19 13:32:05 +12:00 committed by GitHub
parent bff2060454
commit 96bbe70cd8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 56 additions and 0 deletions

View File

@ -49,6 +49,9 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
private static readonly Action<ILogger, string, Exception> _connectionDisposedWhileWriteInProgress =
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(13, "ConnectionDisposedWhileWriteInProgress"), "Connection {TransportConnectionId} was disposed while a write was in progress.");
private static readonly Action<ILogger, string, Exception> _failedToReadHttpRequestBody =
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(14, "FailedToReadHttpRequestBody"), "Connection {TransportConnectionId} failed to read the HTTP request body.");
public static void ConnectionDisposed(ILogger logger, string connectionId)
{
_connectionDisposed(logger, connectionId, null);
@ -113,6 +116,11 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
{
_connectionDisposedWhileWriteInProgress(logger, connectionId, ex);
}
public static void FailedToReadHttpRequestBody(ILogger logger, string connectionId, Exception ex)
{
_failedToReadHttpRequestBody(logger, connectionId, ex);
}
}
}
}

View File

@ -505,6 +505,15 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
context.Response.ContentType = "text/plain";
return;
}
catch (IOException ex)
{
// Can occur when the HTTP request is canceled by the client
Log.FailedToReadHttpRequestBody(_logger, connection.ConnectionId, ex);
context.Response.StatusCode = StatusCodes.Status400BadRequest;
context.Response.ContentType = "text/plain";
return;
}
Log.ReceivedBytes(_logger, connection.ApplicationStream.Length);
}

View File

@ -25,6 +25,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
using Microsoft.Extensions.Primitives;
using Moq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Xunit;
@ -719,6 +720,44 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
}
}
[Theory]
[InlineData(HttpTransportType.LongPolling)]
[InlineData(HttpTransportType.ServerSentEvents)]
public async Task IOExceptionWhenReadingRequestReturns400Response(HttpTransportType transportType)
{
using (StartVerifiableLog(out var loggerFactory, LogLevel.Debug))
{
var manager = CreateConnectionManager(loggerFactory);
var dispatcher = new HttpConnectionDispatcher(manager, loggerFactory);
var connection = manager.CreateConnection();
connection.TransportType = transportType;
var mockStream = new Mock<Stream>();
mockStream.Setup(m => m.CopyToAsync(It.IsAny<Stream>(), It.IsAny<int>(), It.IsAny<CancellationToken>())).Throws(new IOException());
using (var responseBody = new MemoryStream())
{
var context = new DefaultHttpContext();
context.Request.Body = mockStream.Object;
context.Response.Body = responseBody;
var services = new ServiceCollection();
services.AddSingleton<TestConnectionHandler>();
services.AddOptions();
context.Request.Path = "/foo";
context.Request.Method = "POST";
var values = new Dictionary<string, StringValues>();
values["id"] = connection.ConnectionId;
var qs = new QueryCollection(values);
context.Request.Query = qs;
await dispatcher.ExecuteAsync(context, new HttpConnectionDispatcherOptions(), c => Task.CompletedTask);
Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode);
}
}
}
[Fact]
public async Task EndpointsThatRequireConnectionId400WhenNoConnectionIdProvidedForPost()
{