React to pool cleanup (#2526)

This commit is contained in:
Pavel Krymets 2018-04-30 08:37:15 -07:00 committed by GitHub
parent b565a2e33c
commit 2f2a0047a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 400 additions and 50 deletions

View File

@ -7,6 +7,10 @@
<Import Project="build\dependencies.props" /> <Import Project="build\dependencies.props" />
<Import Project="build\sources.props" /> <Import Project="build\sources.props" />
<PropertyGroup>
<DefineConstants Condition="'$(InnerLoop)' != ''">$(DefineConstants);INNER_LOOP</DefineConstants>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<Product>Microsoft ASP.NET Core</Product> <Product>Microsoft ASP.NET Core</Product>
<RepositoryUrl>https://github.com/aspnet/KestrelHttpServer</RepositoryUrl> <RepositoryUrl>https://github.com/aspnet/KestrelHttpServer</RepositoryUrl>

View File

@ -26,6 +26,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
private MemoryPool<byte> _memoryPool; private MemoryPool<byte> _memoryPool;
private DuplexPipe.DuplexPipePair _pair;
[Params( [Params(
BenchmarkTypes.TechEmpowerPlaintext, BenchmarkTypes.TechEmpowerPlaintext,
BenchmarkTypes.PlaintextChunked, BenchmarkTypes.PlaintextChunked,
@ -115,7 +117,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
{ {
_memoryPool = KestrelMemoryPool.Create(); _memoryPool = KestrelMemoryPool.Create();
var options = new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); var options = new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false);
var pair = DuplexPipe.CreateConnectionPair(options, options); _pair = DuplexPipe.CreateConnectionPair(options, options);
var serviceContext = new ServiceContext var serviceContext = new ServiceContext
{ {
@ -131,8 +133,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
ConnectionFeatures = new FeatureCollection(), ConnectionFeatures = new FeatureCollection(),
MemoryPool = _memoryPool, MemoryPool = _memoryPool,
TimeoutControl = new MockTimeoutControl(), TimeoutControl = new MockTimeoutControl(),
Application = pair.Application, Application = _pair.Application,
Transport = pair.Transport Transport = _pair.Transport
}); });
http1Connection.Reset(); http1Connection.Reset();
@ -143,6 +145,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
[IterationCleanup] [IterationCleanup]
public void Cleanup() public void Cleanup()
{ {
_pair.Application.Input.Complete();
_pair.Application.Output.Complete();
_pair.Transport.Input.Complete();
_pair.Transport.Output.Complete();
_memoryPool.Dispose(); _memoryPool.Dispose();
} }

View File

@ -18,7 +18,7 @@
<MicrosoftAspNetCoreTestingPackageVersion>2.2.0-preview1-34066</MicrosoftAspNetCoreTestingPackageVersion> <MicrosoftAspNetCoreTestingPackageVersion>2.2.0-preview1-34066</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftAspNetCoreWebUtilitiesPackageVersion>2.2.0-preview1-34066</MicrosoftAspNetCoreWebUtilitiesPackageVersion> <MicrosoftAspNetCoreWebUtilitiesPackageVersion>2.2.0-preview1-34066</MicrosoftAspNetCoreWebUtilitiesPackageVersion>
<MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>2.2.0-preview1-34066</MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion> <MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>2.2.0-preview1-34066</MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>
<MicrosoftExtensionsBuffersMemoryPoolSourcesPackageVersion>2.2.0-preview1-34066</MicrosoftExtensionsBuffersMemoryPoolSourcesPackageVersion> <MicrosoftExtensionsBuffersMemoryPoolSourcesPackageVersion>2.2.0-a-preview1-pool-cleanup-16484</MicrosoftExtensionsBuffersMemoryPoolSourcesPackageVersion>
<MicrosoftExtensionsBuffersSourcesPackageVersion>2.2.0-preview1-34066</MicrosoftExtensionsBuffersSourcesPackageVersion> <MicrosoftExtensionsBuffersSourcesPackageVersion>2.2.0-preview1-34066</MicrosoftExtensionsBuffersSourcesPackageVersion>
<MicrosoftExtensionsBuffersTestingSourcesPackageVersion>2.2.0-preview1-34066</MicrosoftExtensionsBuffersTestingSourcesPackageVersion> <MicrosoftExtensionsBuffersTestingSourcesPackageVersion>2.2.0-preview1-34066</MicrosoftExtensionsBuffersTestingSourcesPackageVersion>
<MicrosoftExtensionsConfigurationBinderPackageVersion>2.2.0-preview1-34066</MicrosoftExtensionsConfigurationBinderPackageVersion> <MicrosoftExtensionsConfigurationBinderPackageVersion>2.2.0-preview1-34066</MicrosoftExtensionsConfigurationBinderPackageVersion>

View File

@ -1,8 +1,5 @@
<Project> <Project>
<Import Project="..\Directory.Build.props" /> <Import Project="..\Directory.Build.props" />
<PropertyGroup>
<DefineConstants Condition="'$(InnerLoop)' != ''">$(DefineConstants);INNER_LOOP</DefineConstants>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Internal.AspNetCore.Sdk" PrivateAssets="All" Version="$(InternalAspNetCoreSdkPackageVersion)" /> <PackageReference Include="Internal.AspNetCore.Sdk" PrivateAssets="All" Version="$(InternalAspNetCoreSdkPackageVersion)" />
</ItemGroup> </ItemGroup>

View File

@ -148,6 +148,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal
private void Log(string method, ReadOnlySpan<byte> buffer) private void Log(string method, ReadOnlySpan<byte> buffer)
{ {
if (!_logger.IsEnabled(LogLevel.Debug))
{
return;
}
var builder = new StringBuilder($"{method}[{buffer.Length}] "); var builder = new StringBuilder($"{method}[{buffer.Length}] ");
// Write the hex // Write the hex

View File

@ -1,13 +1,26 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Buffers; using System.Buffers;
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal
{ {
public static class KestrelMemoryPool public static class KestrelMemoryPool
{ {
public static MemoryPool<byte> Create() => new SlabMemoryPool(); public static MemoryPool<byte> Create()
{
#if DEBUG
return new DiagnosticMemoryPool(CreateSlabMemoryPool());
#else
return CreateSlabMemoryPool();
#endif
}
public static MemoryPool<byte> CreateSlabMemoryPool()
{
return new SlabMemoryPool();
}
public static readonly int MinimumSegmentSize = 4096; public static readonly int MinimumSegmentSize = 4096;
} }

View File

@ -0,0 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Libuv.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

View File

@ -38,7 +38,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
private readonly object _startSync = new object(); private readonly object _startSync = new object();
private bool _stopImmediate = false; private bool _stopImmediate = false;
private bool _initCompleted = false; private bool _initCompleted = false;
private ExceptionDispatchInfo _closeError; private Exception _closeError;
private readonly ILibuvTrace _log; private readonly ILibuvTrace _log;
public LibuvThread(LibuvTransport transport) public LibuvThread(LibuvTransport transport)
@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
#endif #endif
QueueCloseHandle = PostCloseHandle; QueueCloseHandle = PostCloseHandle;
QueueCloseAsyncHandle = EnqueueCloseHandle; QueueCloseAsyncHandle = EnqueueCloseHandle;
MemoryPool = KestrelMemoryPool.Create(); MemoryPool = transport.TransportOptions.MemoryPoolFactory();
WriteReqPool = new WriteReqPool(this, _log); WriteReqPool = new WriteReqPool(this, _log);
} }
@ -82,7 +82,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
public List<WeakReference> Requests { get; } = new List<WeakReference>(); public List<WeakReference> Requests { get; } = new List<WeakReference>();
#endif #endif
public ExceptionDispatchInfo FatalError { get { return _closeError; } } public Exception FatalError => _closeError;
public Action<Action<IntPtr>, IntPtr> QueueCloseHandle { get; } public Action<Action<IntPtr>, IntPtr> QueueCloseHandle { get; }
@ -133,7 +133,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
} }
} }
_closeError?.Throw(); if (_closeError != null)
{
ExceptionDispatchInfo.Capture(_closeError).Throw();
}
} }
#if DEBUG && !INNER_LOOP #if DEBUG && !INNER_LOOP
@ -323,14 +326,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
} }
catch (Exception ex) catch (Exception ex)
{ {
_closeError = ExceptionDispatchInfo.Capture(ex); _closeError = ex;
// Request shutdown so we can rethrow this exception // Request shutdown so we can rethrow this exception
// in Stop which should be observable. // in Stop which should be observable.
_appLifetime.StopApplication(); _appLifetime.StopApplication();
} }
finally finally
{ {
MemoryPool.Dispose(); try
{
MemoryPool.Dispose();
}
catch (Exception ex)
{
_closeError = _closeError == null ? ex : new AggregateException(_closeError, ex);
}
WriteReqPool.Dispose(); WriteReqPool.Dispose();
_threadTcs.SetResult(null); _threadTcs.SetResult(null);

View File

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Buffers;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv
{ {
@ -18,6 +20,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv
/// </remarks> /// </remarks>
public int ThreadCount { get; set; } = ProcessorThreadCount; public int ThreadCount { get; set; } = ProcessorThreadCount;
internal Func<MemoryPool<byte>> MemoryPoolFactory { get; set; } = () => KestrelMemoryPool.Create();
private static int ProcessorThreadCount private static int ProcessorThreadCount
{ {
get get

View File

@ -0,0 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Sockets.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
{ {
private static readonly PipeScheduler[] ThreadPoolSchedulerArray = new PipeScheduler[] { PipeScheduler.ThreadPool }; private static readonly PipeScheduler[] ThreadPoolSchedulerArray = new PipeScheduler[] { PipeScheduler.ThreadPool };
private readonly MemoryPool<byte> _memoryPool = KestrelMemoryPool.Create(); private readonly MemoryPool<byte> _memoryPool;
private readonly IEndPointInformation _endPointInformation; private readonly IEndPointInformation _endPointInformation;
private readonly IConnectionDispatcher _dispatcher; private readonly IConnectionDispatcher _dispatcher;
private readonly IApplicationLifetime _appLifetime; private readonly IApplicationLifetime _appLifetime;
@ -39,7 +39,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
IConnectionDispatcher dispatcher, IConnectionDispatcher dispatcher,
IApplicationLifetime applicationLifetime, IApplicationLifetime applicationLifetime,
int ioQueueCount, int ioQueueCount,
ISocketsTrace trace) ISocketsTrace trace,
MemoryPool<byte> memoryPool)
{ {
Debug.Assert(endPointInformation != null); Debug.Assert(endPointInformation != null);
Debug.Assert(endPointInformation.Type == ListenType.IPEndPoint); Debug.Assert(endPointInformation.Type == ListenType.IPEndPoint);
@ -51,6 +52,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
_dispatcher = dispatcher; _dispatcher = dispatcher;
_appLifetime = applicationLifetime; _appLifetime = applicationLifetime;
_trace = trace; _trace = trace;
_memoryPool = memoryPool;
if (ioQueueCount > 0) if (ioQueueCount > 0)
{ {

View File

@ -57,7 +57,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
throw new ArgumentNullException(nameof(dispatcher)); throw new ArgumentNullException(nameof(dispatcher));
} }
return new SocketTransport(endPointInformation, dispatcher, _appLifetime, _options.IOQueueCount, _trace); return new SocketTransport(endPointInformation, dispatcher, _appLifetime, _options.IOQueueCount, _trace, _options.MemoryPoolFactory());
} }
} }
} }

View File

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Buffers;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
{ {
@ -14,5 +16,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
/// Defaults to <see cref="Environment.ProcessorCount" /> rounded down and clamped between 1 and 16. /// Defaults to <see cref="Environment.ProcessorCount" /> rounded down and clamped between 1 and 16.
/// </remarks> /// </remarks>
public int IOQueueCount { get; set; } = Math.Min(Environment.ProcessorCount, 16); public int IOQueueCount { get; set; } = Math.Min(Environment.ProcessorCount, 16);
internal Func<MemoryPool<byte>> MemoryPoolFactory { get; set; } = () => KestrelMemoryPool.Create();
} }
} }

View File

@ -287,6 +287,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
public void Dispose() public void Dispose()
{ {
_pair.Application.Input.Complete();
_pair.Application.Output.Complete();
_pair.Transport.Input.Complete();
_pair.Transport.Output.Complete();
_memoryPool.Dispose(); _memoryPool.Dispose();
} }
@ -1213,7 +1217,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
withLength: 0, withLength: 0,
withFlags: (byte)Http2DataFrameFlags.END_STREAM, withFlags: (byte)Http2DataFrameFlags.END_STREAM,
withStreamId: 1); withStreamId: 1);
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false); await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
_hpackDecoder.Decode(headersFrame.HeadersPayload, endHeaders: false, handler: this); _hpackDecoder.Decode(headersFrame.HeadersPayload, endHeaders: false, handler: this);

View File

@ -26,6 +26,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
public void Dispose() public void Dispose()
{ {
_pipe.Reader.Complete();
_pipe.Writer.Complete();
_memoryPool.Dispose(); _memoryPool.Dispose();
} }

View File

@ -68,6 +68,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
public void Dispose() public void Dispose()
{ {
Application.Input.Complete();
Application.Output.Complete();
Transport.Input.Complete();
Transport.Output.Complete();
_memoryPool.Dispose(); _memoryPool.Dispose();
} }
} }

View File

@ -12,11 +12,16 @@ using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Logging.Testing; using Microsoft.Extensions.Logging.Testing;
using Xunit; using Xunit;
using Xunit.Abstractions;
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
public class ChunkedRequestTests : LoggedTest public class ChunkedRequestTests : LoggedTest
{ {
public ChunkedRequestTests(ITestOutputHelper output) : base(output)
{
}
public static TheoryData<ListenOptions> ConnectionAdapterData => new TheoryData<ListenOptions> public static TheoryData<ListenOptions> ConnectionAdapterData => new TheoryData<ListenOptions>
{ {
new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)), new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)),
@ -88,7 +93,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
[MemberData(nameof(ConnectionAdapterData))] [MemberData(nameof(ConnectionAdapterData))]
public async Task Http10KeepAliveTransferEncoding(ListenOptions listenOptions) public async Task Http10KeepAliveTransferEncoding(ListenOptions listenOptions)
{ {
var testContext = new TestServiceContext(LoggerFactory); var testContext = new TestServiceContext();
using (var server = new TestServer(AppChunked, testContext, listenOptions)) using (var server = new TestServer(AppChunked, testContext, listenOptions))
{ {

View File

@ -0,0 +1,46 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
public class DiagnosticMemoryPoolFactory
{
private readonly bool _allowLateReturn;
private readonly bool _rentTracking;
private readonly List<DiagnosticMemoryPool> _pools;
public DiagnosticMemoryPoolFactory(bool allowLateReturn = false, bool rentTracking = false)
{
_allowLateReturn = allowLateReturn;
_rentTracking = rentTracking;
_pools = new List<DiagnosticMemoryPool>();
}
public MemoryPool<byte> Create()
{
lock (_pools)
{
var pool = new DiagnosticMemoryPool(KestrelMemoryPool.CreateSlabMemoryPool(),_allowLateReturn, _rentTracking);
_pools.Add(pool);
return pool;
}
}
public Task WhenAllBlocksReturned(TimeSpan span)
{
lock (_pools)
{
return Task.WhenAll(_pools.Select(p=>p.WhenAllBlocksReturnedAsync(span)));
}
}
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -90,7 +91,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
var clientFinishedSendingRequestBody = new TaskCompletionSource<object>(); var clientFinishedSendingRequestBody = new TaskCompletionSource<object>();
var lastBytesWritten = DateTime.MaxValue; var lastBytesWritten = DateTime.MaxValue;
using (var host = StartWebHost(maxRequestBufferSize, data, connectionAdapter, startReadingRequestBody, clientFinishedSendingRequestBody)) var memoryPoolFactory = new DiagnosticMemoryPoolFactory(allowLateReturn: true);
using (var host = StartWebHost(maxRequestBufferSize, data, connectionAdapter, startReadingRequestBody, clientFinishedSendingRequestBody, memoryPoolFactory.Create))
{ {
var port = host.GetPort(); var port = host.GetPort();
using (var socket = CreateSocket(port)) using (var socket = CreateSocket(port))
@ -167,6 +170,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
} }
} }
} }
await memoryPoolFactory.WhenAllBlocksReturned(TestConstants.DefaultTimeout);
} }
[Fact] [Fact]
@ -182,7 +187,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
var clientFinishedSendingRequestBody = new TaskCompletionSource<object>(); var clientFinishedSendingRequestBody = new TaskCompletionSource<object>();
var lastBytesWritten = DateTime.MaxValue; var lastBytesWritten = DateTime.MaxValue;
using (var host = StartWebHost(16 * 1024, data, false, startReadingRequestBody, clientFinishedSendingRequestBody)) var memoryPoolFactory = new DiagnosticMemoryPoolFactory(allowLateReturn: true);
using (var host = StartWebHost(16 * 1024, data, false, startReadingRequestBody, clientFinishedSendingRequestBody, memoryPoolFactory.Create))
{ {
var port = host.GetPort(); var port = host.GetPort();
using (var socket = CreateSocket(port)) using (var socket = CreateSocket(port))
@ -239,15 +246,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
host.Dispose(); host.Dispose();
} }
} }
// Allow appfunc to unblock
startReadingRequestBody.SetResult(null);
clientFinishedSendingRequestBody.SetResult(null);
await memoryPoolFactory.WhenAllBlocksReturned(TestConstants.DefaultTimeout);
} }
private IWebHost StartWebHost(long? maxRequestBufferSize, private IWebHost StartWebHost(long? maxRequestBufferSize,
byte[] expectedBody, byte[] expectedBody,
bool useConnectionAdapter, bool useConnectionAdapter,
TaskCompletionSource<object> startReadingRequestBody, TaskCompletionSource<object> startReadingRequestBody,
TaskCompletionSource<object> clientFinishedSendingRequestBody) TaskCompletionSource<object> clientFinishedSendingRequestBody,
Func<MemoryPool<byte>> memoryPoolFactory = null)
{ {
var host = TransportSelector.GetWebHostBuilder() var host = TransportSelector.GetWebHostBuilder(memoryPoolFactory)
.ConfigureServices(AddTestLogging) .ConfigureServices(AddTestLogging)
.UseKestrel(options => .UseKestrel(options =>
{ {

View File

@ -590,9 +590,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
[InlineData("http://localhost/path%20with%20space?q=abc%20123", "/path with space", "abc 123")] [InlineData("http://localhost/path%20with%20space?q=abc%20123", "/path with space", "abc 123")]
public async Task CanHandleRequestsWithUrlInAbsoluteForm(string requestUrl, string expectedPath, string queryValue) public async Task CanHandleRequestsWithUrlInAbsoluteForm(string requestUrl, string expectedPath, string queryValue)
{ {
var pathTcs = new TaskCompletionSource<PathString>(); var pathTcs = new TaskCompletionSource<PathString>(TaskCreationOptions.RunContinuationsAsynchronously);
var rawTargetTcs = new TaskCompletionSource<string>(); var rawTargetTcs = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
var queryTcs = new TaskCompletionSource<IQueryCollection>(); var queryTcs = new TaskCompletionSource<IQueryCollection>(TaskCreationOptions.RunContinuationsAsynchronously);
using (var server = new TestServer(async context => using (var server = new TestServer(async context =>
{ {
@ -1031,8 +1031,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
var testContext = new TestServiceContext(LoggerFactory); var testContext = new TestServiceContext(LoggerFactory);
var readTcs = new TaskCompletionSource<object>(); var readTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var registrationTcs = new TaskCompletionSource<int>(); var registrationTcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
var requestId = 0; var requestId = 0;
using (var server = new TestServer(async httpContext => using (var server = new TestServer(async httpContext =>

View File

@ -33,6 +33,7 @@ using Microsoft.Extensions.Logging.Testing;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
using Moq; using Moq;
using Xunit; using Xunit;
using Xunit.Abstractions;
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
@ -145,7 +146,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
public async Task OnCompleteCalledEvenWhenOnStartingNotCalled() public async Task OnCompleteCalledEvenWhenOnStartingNotCalled()
{ {
var onStartingCalled = false; var onStartingCalled = false;
var onCompletedTcs = new TaskCompletionSource<object>(); var onCompletedTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var hostBuilder = TransportSelector.GetWebHostBuilder() var hostBuilder = TransportSelector.GetWebHostBuilder()
.UseKestrel() .UseKestrel()
@ -341,7 +342,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
[Fact] [Fact]
public async Task OnCompletedShouldNotBlockAResponse() public async Task OnCompletedShouldNotBlockAResponse()
{ {
var delayTcs = new TaskCompletionSource<object>(); var delayTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var hostBuilder = TransportSelector.GetWebHostBuilder() var hostBuilder = TransportSelector.GetWebHostBuilder()
.UseKestrel() .UseKestrel()
.UseUrls("http://127.0.0.1:0/") .UseUrls("http://127.0.0.1:0/")
@ -375,7 +376,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
[Fact] [Fact]
public async Task InvalidChunkedEncodingInRequestShouldNotBlockOnCompleted() public async Task InvalidChunkedEncodingInRequestShouldNotBlockOnCompleted()
{ {
var onCompletedTcs = new TaskCompletionSource<object>(); var onCompletedTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
using (var server = new TestServer(httpContext => using (var server = new TestServer(httpContext =>
{ {
@ -418,7 +419,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
mockHttpContextFactory.Setup(f => f.Create(It.IsAny<IFeatureCollection>())) mockHttpContextFactory.Setup(f => f.Create(It.IsAny<IFeatureCollection>()))
.Returns<IFeatureCollection>(fc => new DefaultHttpContext(fc)); .Returns<IFeatureCollection>(fc => new DefaultHttpContext(fc));
var disposedTcs = new TaskCompletionSource<int>(); var disposedTcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
mockHttpContextFactory.Setup(f => f.Dispose(It.IsAny<HttpContext>())) mockHttpContextFactory.Setup(f => f.Dispose(It.IsAny<HttpContext>()))
.Callback<HttpContext>(c => .Callback<HttpContext>(c =>
{ {
@ -619,7 +620,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
const string response = "hello, world"; const string response = "hello, world";
var logTcs = new TaskCompletionSource<object>(); var logTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var mockKestrelTrace = new Mock<IKestrelTrace>(); var mockKestrelTrace = new Mock<IKestrelTrace>();
mockKestrelTrace mockKestrelTrace
.Setup(trace => trace.ConnectionHeadResponseBodyWrite(It.IsAny<string>(), response.Length)) .Setup(trace => trace.ConnectionHeadResponseBodyWrite(It.IsAny<string>(), response.Length))
@ -808,7 +809,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
[Fact] [Fact]
public async Task WhenAppWritesLessThanContentLengthErrorLogged() public async Task WhenAppWritesLessThanContentLengthErrorLogged()
{ {
var logTcs = new TaskCompletionSource<object>(); var logTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var mockTrace = new Mock<IKestrelTrace>(); var mockTrace = new Mock<IKestrelTrace>();
mockTrace mockTrace
.Setup(trace => trace.ApplicationError(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<InvalidOperationException>())) .Setup(trace => trace.ApplicationError(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<InvalidOperationException>()))
@ -1182,7 +1183,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
var connectionClosed = new ManualResetEventSlim(); var connectionClosed = new ManualResetEventSlim();
var requestStarted = new ManualResetEventSlim(); var requestStarted = new ManualResetEventSlim();
var tcs = new TaskCompletionSource<object>(); var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
using (var server = new TestServer(async httpContext => using (var server = new TestServer(async httpContext =>
{ {
@ -2235,7 +2236,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
// Ensure string is long enough to disable write-behind buffering // Ensure string is long enough to disable write-behind buffering
var largeString = new string('a', maxBytesPreCompleted + 1); var largeString = new string('a', maxBytesPreCompleted + 1);
var writeTcs = new TaskCompletionSource<object>(); var writeTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var requestAbortedWh = new ManualResetEventSlim(); var requestAbortedWh = new ManualResetEventSlim();
var requestStartWh = new ManualResetEventSlim(); var requestStartWh = new ManualResetEventSlim();
@ -2400,7 +2401,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
var testContext= new TestServiceContext(LoggerFactory); var testContext= new TestServiceContext(LoggerFactory);
var callOrder = new Stack<int>(); var callOrder = new Stack<int>();
var onStartingTcs = new TaskCompletionSource<object>(); var onStartingTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
using (var server = new TestServer(async context => using (var server = new TestServer(async context =>
{ {
@ -2452,7 +2453,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
var testContext= new TestServiceContext(LoggerFactory); var testContext= new TestServiceContext(LoggerFactory);
var callOrder = new Stack<int>(); var callOrder = new Stack<int>();
var onCompletedTcs = new TaskCompletionSource<object>(); var onCompletedTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
using (var server = new TestServer(async context => using (var server = new TestServer(async context =>
{ {

View File

@ -1,15 +1,17 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Buffers;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
public static class TransportSelector public static class TransportSelector
{ {
public static IWebHostBuilder GetWebHostBuilder() public static IWebHostBuilder GetWebHostBuilder(Func<MemoryPool<byte>> memoryPoolFactory = null)
{ {
return new WebHostBuilder().UseLibuv().ConfigureServices(TestServer.RemoveDevCert); return new WebHostBuilder().UseLibuv(options => { options.MemoryPoolFactory = memoryPoolFactory ?? options.MemoryPoolFactory; }).ConfigureServices(TestServer.RemoveDevCert);
} }
} }
} }

View File

@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
var transportContext = new TestLibuvTransportContext() { ConnectionDispatcher = mockConnectionDispatcher }; var transportContext = new TestLibuvTransportContext() { ConnectionDispatcher = mockConnectionDispatcher };
var transport = new LibuvTransport(mockLibuv, transportContext, null); var transport = new LibuvTransport(mockLibuv, transportContext, null);
var thread = new LibuvThread(transport); var thread = new LibuvThread(transport);
Task connectionTask = null;
try try
{ {
await thread.StartAsync(); await thread.StartAsync();
@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
}; };
var socket = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, transportContext.Log); var socket = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, transportContext.Log);
var connection = new LibuvConnection(listenerContext, socket); var connection = new LibuvConnection(listenerContext, socket);
_ = connection.Start(); connectionTask = connection.Start();
mockLibuv.AllocCallback(socket.InternalGetHandle(), 2048, out var ignored); mockLibuv.AllocCallback(socket.InternalGetHandle(), 2048, out var ignored);
mockLibuv.ReadCallback(socket.InternalGetHandle(), 0, ref ignored); mockLibuv.ReadCallback(socket.InternalGetHandle(), 0, ref ignored);
@ -47,6 +47,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
} }
finally finally
{ {
mockConnectionDispatcher.Input.Reader.Complete();
mockConnectionDispatcher.Output.Writer.Complete();
await connectionTask;
await thread.StopAsync(TimeSpan.FromSeconds(5)); await thread.StopAsync(TimeSpan.FromSeconds(5));
} }
} }
@ -108,6 +112,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
} }
finally finally
{ {
mockConnectionDispatcher.Input.Reader.Complete();
mockConnectionDispatcher.Output.Writer.Complete();
await thread.StopAsync(TimeSpan.FromSeconds(5)); await thread.StopAsync(TimeSpan.FromSeconds(5));
} }
} }
@ -189,6 +196,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
} }
finally finally
{ {
mockConnectionDispatcher.Input.Reader.Complete();
mockConnectionDispatcher.Output.Writer.Complete();
await thread.StopAsync(TimeSpan.FromSeconds(5)); await thread.StopAsync(TimeSpan.FromSeconds(5));
} }
} }
@ -202,6 +212,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
var transport = new LibuvTransport(mockLibuv, transportContext, null); var transport = new LibuvTransport(mockLibuv, transportContext, null);
var thread = new LibuvThread(transport); var thread = new LibuvThread(transport);
Task connectionTask = null;
try try
{ {
await thread.StartAsync(); await thread.StartAsync();
@ -213,7 +224,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
}; };
var socket = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, transportContext.Log); var socket = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, transportContext.Log);
var connection = new LibuvConnection(listenerContext, socket); var connection = new LibuvConnection(listenerContext, socket);
_ = connection.Start(); connectionTask = connection.Start();
var ignored = new LibuvFunctions.uv_buf_t(); var ignored = new LibuvFunctions.uv_buf_t();
mockLibuv.ReadCallback(socket.InternalGetHandle(), TestConstants.EOF, ref ignored); mockLibuv.ReadCallback(socket.InternalGetHandle(), TestConstants.EOF, ref ignored);
@ -224,6 +235,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
} }
finally finally
{ {
mockConnectionDispatcher.Input.Reader.Complete();
mockConnectionDispatcher.Output.Writer.Complete();
await connectionTask;
await thread.StopAsync(TimeSpan.FromSeconds(5)); await thread.StopAsync(TimeSpan.FromSeconds(5));
} }
} }

View File

@ -1,12 +1,14 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core;
@ -85,6 +87,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
} }
} }
Assert.True(await serviceContext.ConnectionManager.CloseAllConnectionsAsync(new CancellationTokenSource(TestConstants.DefaultTimeout).Token));
await transport.UnbindAsync(); await transport.UnbindAsync();
await transport.StopAsync(); await transport.StopAsync();
} }

View File

@ -1,15 +1,17 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Buffers;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{ {
public static class TransportSelector public static class TransportSelector
{ {
public static IWebHostBuilder GetWebHostBuilder() public static IWebHostBuilder GetWebHostBuilder(Func<MemoryPool<byte>> memoryPoolFactory = null)
{ {
return new WebHostBuilder().UseSockets().ConfigureServices(TestServer.RemoveDevCert); return new WebHostBuilder().UseSockets(options => { options.MemoryPoolFactory = memoryPoolFactory ?? options.MemoryPoolFactory; }).ConfigureServices(TestServer.RemoveDevCert);
} }
} }
} }

View File

@ -0,0 +1,187 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO.Pipelines;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Testing
{
public class CompositeKestrelTrace: IKestrelTrace
{
private readonly IKestrelTrace _trace1;
private readonly IKestrelTrace _trace2;
public CompositeKestrelTrace(IKestrelTrace kestrelTrace, KestrelTrace kestrelTrace1)
{
_trace1 = kestrelTrace;
_trace2 = kestrelTrace1;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
_trace1.Log(logLevel, eventId, state, exception, formatter);
_trace2.Log(logLevel, eventId, state, exception, formatter);
}
public bool IsEnabled(LogLevel logLevel)
{
return _trace1.IsEnabled(logLevel) || _trace2.IsEnabled(logLevel);
}
public IDisposable BeginScope<TState>(TState state)
{
return _trace1.BeginScope(state);
}
public void ConnectionStart(string connectionId)
{
_trace1.ConnectionStart(connectionId);
_trace2.ConnectionStart(connectionId);
}
public void ConnectionStop(string connectionId)
{
_trace1.ConnectionStop(connectionId);
_trace2.ConnectionStop(connectionId);
}
public void ConnectionPause(string connectionId)
{
_trace1.ConnectionPause(connectionId);
_trace2.ConnectionPause(connectionId);
}
public void ConnectionResume(string connectionId)
{
_trace1.ConnectionResume(connectionId);
_trace2.ConnectionResume(connectionId);
}
public void ConnectionRejected(string connectionId)
{
_trace1.ConnectionRejected(connectionId);
_trace2.ConnectionRejected(connectionId);
}
public void ConnectionKeepAlive(string connectionId)
{
_trace1.ConnectionKeepAlive(connectionId);
_trace2.ConnectionKeepAlive(connectionId);
}
public void ConnectionDisconnect(string connectionId)
{
_trace1.ConnectionDisconnect(connectionId);
_trace2.ConnectionDisconnect(connectionId);
}
public void RequestProcessingError(string connectionId, Exception ex)
{
_trace1.RequestProcessingError(connectionId, ex);
_trace2.RequestProcessingError(connectionId, ex);
}
public void ConnectionHeadResponseBodyWrite(string connectionId, long count)
{
_trace1.ConnectionHeadResponseBodyWrite(connectionId, count);
_trace2.ConnectionHeadResponseBodyWrite(connectionId, count);
}
public void NotAllConnectionsClosedGracefully()
{
_trace1.NotAllConnectionsClosedGracefully();
_trace2.NotAllConnectionsClosedGracefully();
}
public void ConnectionBadRequest(string connectionId, BadHttpRequestException ex)
{
_trace1.ConnectionBadRequest(connectionId, ex);
_trace2.ConnectionBadRequest(connectionId, ex);
}
public void ApplicationError(string connectionId, string traceIdentifier, Exception ex)
{
_trace1.ApplicationError(connectionId, traceIdentifier, ex);
_trace2.ApplicationError(connectionId, traceIdentifier, ex);
}
public void NotAllConnectionsAborted()
{
_trace1.NotAllConnectionsAborted();
_trace2.NotAllConnectionsAborted();
}
public void HeartbeatSlow(TimeSpan interval, DateTimeOffset now)
{
_trace1.HeartbeatSlow(interval, now);
_trace2.HeartbeatSlow(interval, now);
}
public void ApplicationNeverCompleted(string connectionId)
{
_trace1.ApplicationNeverCompleted(connectionId);
_trace2.ApplicationNeverCompleted(connectionId);
}
public void RequestBodyStart(string connectionId, string traceIdentifier)
{
_trace1.RequestBodyStart(connectionId, traceIdentifier);
_trace2.RequestBodyStart(connectionId, traceIdentifier);
}
public void RequestBodyDone(string connectionId, string traceIdentifier)
{
_trace1.RequestBodyDone(connectionId, traceIdentifier);
_trace2.RequestBodyDone(connectionId, traceIdentifier);
}
public void RequestBodyNotEntirelyRead(string connectionId, string traceIdentifier)
{
_trace1.RequestBodyNotEntirelyRead(connectionId, traceIdentifier);
_trace2.RequestBodyNotEntirelyRead(connectionId, traceIdentifier);
}
public void RequestBodyDrainTimedOut(string connectionId, string traceIdentifier)
{
_trace1.RequestBodyDrainTimedOut(connectionId, traceIdentifier);
_trace2.RequestBodyDrainTimedOut(connectionId, traceIdentifier);
}
public void RequestBodyMininumDataRateNotSatisfied(string connectionId, string traceIdentifier, double rate)
{
_trace1.RequestBodyMininumDataRateNotSatisfied(connectionId, traceIdentifier, rate);
_trace2.RequestBodyMininumDataRateNotSatisfied(connectionId, traceIdentifier, rate);
}
public void ResponseMininumDataRateNotSatisfied(string connectionId, string traceIdentifier)
{
_trace1.ResponseMininumDataRateNotSatisfied(connectionId, traceIdentifier);
_trace2.ResponseMininumDataRateNotSatisfied(connectionId, traceIdentifier);
}
public void Http2ConnectionError(string connectionId, Http2ConnectionErrorException ex)
{
_trace1.Http2ConnectionError(connectionId, ex);
_trace2.Http2ConnectionError(connectionId, ex);
}
public void Http2StreamError(string connectionId, Http2StreamErrorException ex)
{
_trace1.Http2StreamError(connectionId, ex);
_trace2.Http2StreamError(connectionId, ex);
}
public void HPackDecodingError(string connectionId, int streamId, HPackDecodingException ex)
{
_trace1.HPackDecodingError(connectionId, streamId, ex);
_trace2.HPackDecodingError(connectionId, streamId, ex);
}
}
}

View File

@ -44,15 +44,16 @@ namespace Microsoft.AspNetCore.Testing
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{ {
var exceptionIsIgnored = IgnoredExceptions.Contains(exception?.GetType());
#if true #if true
if (logLevel == LogLevel.Critical && ThrowOnCriticalErrors && !IgnoredExceptions.Contains(exception.GetType())) if (logLevel == LogLevel.Critical && ThrowOnCriticalErrors && !exceptionIsIgnored)
#endif #endif
{ {
var log = $"Log {logLevel}[{eventId}]: {formatter(state, exception)} {exception}"; var log = $"Log {logLevel}[{eventId}]: {formatter(state, exception)} {exception}";
Console.WriteLine(log); Console.WriteLine(log);
if (logLevel == LogLevel.Critical && ThrowOnCriticalErrors && !IgnoredExceptions.Contains(exception.GetType())) if (logLevel == LogLevel.Critical && ThrowOnCriticalErrors && !exceptionIsIgnored)
{ {
throw new Exception($"Unexpected critical error. {log}", exception); throw new Exception($"Unexpected critical error. {log}", exception);
} }

View File

@ -1,10 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO.Pipelines; using System.IO.Pipelines;
using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -22,13 +25,18 @@ namespace Microsoft.AspNetCore.Testing
} }
public TestServiceContext(ILoggerFactory loggerFactory) public TestServiceContext(ILoggerFactory loggerFactory)
: this(loggerFactory, new KestrelTrace(loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel")))
{ {
Initialize(loggerFactory, CreateLoggingTrace(loggerFactory));
} }
public TestServiceContext(ILoggerFactory loggerFactory, IKestrelTrace kestrelTrace) public TestServiceContext(ILoggerFactory loggerFactory, IKestrelTrace kestrelTrace)
{ {
Initialize(loggerFactory, kestrelTrace); Initialize(loggerFactory, new CompositeKestrelTrace(kestrelTrace, CreateLoggingTrace(loggerFactory)));
}
private static KestrelTrace CreateLoggingTrace(ILoggerFactory loggerFactory)
{
return new KestrelTrace(loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel"));
} }
private void Initialize(ILoggerFactory loggerFactory, IKestrelTrace kestrelTrace) private void Initialize(ILoggerFactory loggerFactory, IKestrelTrace kestrelTrace)

View File

@ -2,5 +2,6 @@
"$schema": "http://json.schemastore.org/xunit.runner.schema", "$schema": "http://json.schemastore.org/xunit.runner.schema",
"appDomain": "denied", "appDomain": "denied",
"methodDisplay": "method", "methodDisplay": "method",
"longRunningTestSeconds": 60 "longRunningTestSeconds": 60,
"maxParallelThreads": -1
} }